appium-xcuitest-driver 10.14.10 → 10.14.11
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 +11 -0
- package/build/lib/commands/battery.d.ts.map +1 -1
- package/build/lib/commands/battery.js +2 -1
- package/build/lib/commands/battery.js.map +1 -1
- package/build/lib/commands/condition.d.ts +33 -0
- package/build/lib/commands/condition.d.ts.map +1 -1
- package/build/lib/commands/condition.js +170 -73
- package/build/lib/commands/condition.js.map +1 -1
- package/build/lib/device/remotexpc-utils.d.ts +13 -0
- package/build/lib/device/remotexpc-utils.d.ts.map +1 -0
- package/build/lib/device/remotexpc-utils.js +80 -0
- package/build/lib/device/remotexpc-utils.js.map +1 -0
- package/build/lib/driver.d.ts +2 -3
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +3 -5
- package/build/lib/driver.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/commands/battery.ts +2 -1
- package/lib/commands/condition.ts +236 -86
- package/lib/device/remotexpc-utils.ts +88 -0
- package/lib/driver.ts +4 -6
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
- package/scripts/{build-wda.js → build-wda.mjs} +7 -8
package/lib/commands/battery.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isIos18OrNewer } from '../utils';
|
|
2
2
|
import type {XCUITestDriver} from '../driver';
|
|
3
3
|
import type {BatteryInfo} from './types';
|
|
4
|
+
import {getRemoteXPCServices} from '../device/remotexpc-utils';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Reads the battery information from the device under test.
|
|
@@ -16,7 +17,7 @@ export async function mobileGetBatteryInfo(
|
|
|
16
17
|
if (isIos18OrNewer(this.opts) && this.isRealDevice()) {
|
|
17
18
|
let remoteXPCConnection;
|
|
18
19
|
try {
|
|
19
|
-
const
|
|
20
|
+
const Services = await getRemoteXPCServices();
|
|
20
21
|
const { diagnosticsService, remoteXPC } = await Services.startDiagnosticsService(this.device.udid);
|
|
21
22
|
remoteXPCConnection = remoteXPC;
|
|
22
23
|
batteryInfoFromShimService = await diagnosticsService.ioregistry({
|
|
@@ -2,8 +2,48 @@ import {INSTRUMENT_CHANNEL, services} from 'appium-ios-device';
|
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import { isIos18OrNewer, requireRealDevice } from '../utils';
|
|
4
4
|
import type {XCUITestDriver} from '../driver';
|
|
5
|
+
import type {AppiumLogger} from '@appium/types';
|
|
5
6
|
import type {DVTServiceWithConnection} from 'appium-ios-remotexpc';
|
|
6
7
|
import type {Condition} from './types';
|
|
8
|
+
import {getRemoteXPCServices} from '../device/remotexpc-utils';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Abstract interface for condition inducer implementations.
|
|
12
|
+
* This facade hides the differences between RemoteXPC and Instrument Service implementations.
|
|
13
|
+
*/
|
|
14
|
+
export interface IConditionInducer {
|
|
15
|
+
/**
|
|
16
|
+
* Lists all available condition inducers
|
|
17
|
+
* @returns Array of available condition inducers
|
|
18
|
+
*/
|
|
19
|
+
list(): Promise<Condition[]>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Enables a condition inducer with the given profile
|
|
23
|
+
* @param conditionID - The condition identifier (only used by Instrument Service)
|
|
24
|
+
* @param profileID - The profile identifier
|
|
25
|
+
* @returns `true` if enabling succeeded
|
|
26
|
+
* @throws {Error} If a condition is already active
|
|
27
|
+
*/
|
|
28
|
+
enable(conditionID: string, profileID: string): Promise<boolean>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Disables the currently active condition inducer
|
|
32
|
+
* @returns `true` if disabling succeeded
|
|
33
|
+
*/
|
|
34
|
+
disable(): Promise<boolean>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Closes any open connections/resources
|
|
38
|
+
*/
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a condition inducer is currently active
|
|
43
|
+
* @returns `true` if a condition is active
|
|
44
|
+
*/
|
|
45
|
+
isActive(): boolean;
|
|
46
|
+
}
|
|
7
47
|
|
|
8
48
|
/**
|
|
9
49
|
* Get all available ConditionInducer configuration information, which can be used with
|
|
@@ -14,32 +54,8 @@ import type {Condition} from './types';
|
|
|
14
54
|
export async function listConditionInducers(this: XCUITestDriver): Promise<Condition[]> {
|
|
15
55
|
requireRealDevice(this, 'Condition inducer');
|
|
16
56
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
dvtConnection = await startRemoteXPC(this.device.udid);
|
|
21
|
-
const result = await dvtConnection.conditionInducer.list();
|
|
22
|
-
return result as Condition[];
|
|
23
|
-
} catch (err: any) {
|
|
24
|
-
this.log.error(`Failed to list condition inducers via RemoteXPC: ${err.message}`);
|
|
25
|
-
} finally {
|
|
26
|
-
if (dvtConnection) {
|
|
27
|
-
this.log.info(`Closing remoteXPC connection for device ${this.device.udid}`);
|
|
28
|
-
await dvtConnection.remoteXPC.close();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const conditionInducerService = await services.startInstrumentService(this.device.udid);
|
|
34
|
-
try {
|
|
35
|
-
const ret = await conditionInducerService.callChannel(
|
|
36
|
-
INSTRUMENT_CHANNEL.CONDITION_INDUCER,
|
|
37
|
-
'availableConditionInducers',
|
|
38
|
-
);
|
|
39
|
-
return ret.selector;
|
|
40
|
-
} finally {
|
|
41
|
-
conditionInducerService.close();
|
|
42
|
-
}
|
|
57
|
+
const facade = this._conditionInducer ?? await createConditionInducer(this);
|
|
58
|
+
return await facade.list();
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
/**
|
|
@@ -67,45 +83,26 @@ export async function enableConditionInducer(
|
|
|
67
83
|
): Promise<boolean> {
|
|
68
84
|
requireRealDevice(this, 'Condition inducer');
|
|
69
85
|
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
const dvtConnection = await startRemoteXPC(this.device.udid);
|
|
79
|
-
this._remoteXPCConditionInducerConnection = dvtConnection;
|
|
80
|
-
|
|
81
|
-
await dvtConnection.conditionInducer.set(profileID);
|
|
82
|
-
|
|
83
|
-
this.log.info(`Successfully enabled condition profile: ${profileID}`);
|
|
84
|
-
return true;
|
|
85
|
-
} catch (err: any) {
|
|
86
|
-
await closeRemoteXPC.call(this);
|
|
87
|
-
this.log.error(`Condition inducer '${profileID}' cannot be enabled: '${err.message}'`);
|
|
88
|
-
}
|
|
86
|
+
if (this._conditionInducer?.isActive()) {
|
|
87
|
+
throw this.log.errorWithException(
|
|
88
|
+
`Condition inducer is already running. Disable it first in order to call 'enable' again.`
|
|
89
|
+
);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
const facade = await createConditionInducer(this);
|
|
93
|
+
this._conditionInducer = facade;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
return await facade.enable(conditionID, profileID);
|
|
97
|
+
} catch (err: any) {
|
|
98
|
+
this._conditionInducer = null;
|
|
99
|
+
try {
|
|
100
|
+
await facade.close();
|
|
101
|
+
} catch {}
|
|
92
102
|
throw this.log.errorWithException(
|
|
93
|
-
`Condition inducer
|
|
103
|
+
`Condition inducer '${profileID}' cannot be enabled: '${err.message}'`
|
|
94
104
|
);
|
|
95
105
|
}
|
|
96
|
-
this._conditionInducerService = await services.startInstrumentService(this.device.udid);
|
|
97
|
-
const ret = await this._conditionInducerService.callChannel(
|
|
98
|
-
INSTRUMENT_CHANNEL.CONDITION_INDUCER,
|
|
99
|
-
'enableConditionWithIdentifier:profileIdentifier:',
|
|
100
|
-
conditionID,
|
|
101
|
-
profileID,
|
|
102
|
-
);
|
|
103
|
-
if (!_.isBoolean(ret.selector)) {
|
|
104
|
-
this._conditionInducerService.close();
|
|
105
|
-
this._conditionInducerService = null;
|
|
106
|
-
throw this.log.errorWithException(`Enable condition inducer error: '${JSON.stringify(ret.selector)}'`);
|
|
107
|
-
}
|
|
108
|
-
return ret.selector;
|
|
109
106
|
}
|
|
110
107
|
|
|
111
108
|
/**
|
|
@@ -122,57 +119,210 @@ export async function enableConditionInducer(
|
|
|
122
119
|
export async function disableConditionInducer(this: XCUITestDriver): Promise<boolean> {
|
|
123
120
|
requireRealDevice(this, 'Condition inducer');
|
|
124
121
|
|
|
125
|
-
if (
|
|
126
|
-
|
|
122
|
+
if (!this._conditionInducer) {
|
|
123
|
+
this.log.warn('Condition inducer is not active');
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
return await this._conditionInducer.disable();
|
|
129
|
+
} finally {
|
|
130
|
+
try {
|
|
131
|
+
await this._conditionInducer.close();
|
|
132
|
+
} catch {}
|
|
133
|
+
this._conditionInducer = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Private implementation classes and factory function
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* RemoteXPC-based implementation for iOS 18+
|
|
141
|
+
*/
|
|
142
|
+
class RemoteXPCConditionInducer implements IConditionInducer {
|
|
143
|
+
private connection: DVTServiceWithConnection | null = null;
|
|
144
|
+
|
|
145
|
+
constructor(
|
|
146
|
+
private readonly udid: string,
|
|
147
|
+
private readonly log: AppiumLogger,
|
|
148
|
+
) {}
|
|
149
|
+
|
|
150
|
+
async list(): Promise<Condition[]> {
|
|
151
|
+
let connection: DVTServiceWithConnection | null = null;
|
|
152
|
+
try {
|
|
153
|
+
connection = await this.startConnection();
|
|
154
|
+
const result = await connection.conditionInducer.list();
|
|
155
|
+
return result as Condition[];
|
|
156
|
+
} catch (err: any) {
|
|
157
|
+
this.log.error(`Failed to list condition inducers via RemoteXPC: ${err.message}`);
|
|
158
|
+
throw err;
|
|
159
|
+
} finally {
|
|
160
|
+
if (connection) {
|
|
161
|
+
this.log.info(`Closing remoteXPC connection for device ${this.udid}`);
|
|
162
|
+
await connection.remoteXPC.close();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async enable(conditionID: string, profileID: string): Promise<boolean> {
|
|
168
|
+
if (this.connection) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`Condition inducer is already running. Disable it first in order to call 'enable' again.`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
this.connection = await this.startConnection();
|
|
176
|
+
await this.connection.conditionInducer.set(profileID);
|
|
177
|
+
this.log.info(`Successfully enabled condition profile: ${profileID}`);
|
|
178
|
+
return true;
|
|
179
|
+
} catch (err: any) {
|
|
180
|
+
await this.close();
|
|
181
|
+
this.log.error(`Condition inducer '${profileID}' cannot be enabled: '${err.message}'`);
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async disable(): Promise<boolean> {
|
|
187
|
+
if (!this.connection) {
|
|
127
188
|
this.log.warn('Condition inducer connection is not active');
|
|
128
189
|
return false;
|
|
129
190
|
}
|
|
130
191
|
|
|
131
192
|
try {
|
|
132
|
-
await this.
|
|
193
|
+
await this.connection.conditionInducer.disable();
|
|
133
194
|
this.log.info('Successfully disabled condition inducer');
|
|
134
195
|
return true;
|
|
135
196
|
} catch (err: any) {
|
|
136
197
|
this.log.warn(`Failed to disable condition inducer via RemoteXPC: ${err.message}`);
|
|
137
198
|
return false;
|
|
138
199
|
} finally {
|
|
139
|
-
this.log.info(`Closing remoteXPC connection for device ${this.
|
|
140
|
-
await
|
|
200
|
+
this.log.info(`Closing remoteXPC connection for device ${this.udid}`);
|
|
201
|
+
await this.close();
|
|
141
202
|
}
|
|
142
203
|
}
|
|
143
204
|
|
|
144
|
-
|
|
145
|
-
this.
|
|
146
|
-
|
|
205
|
+
async close(): Promise<void> {
|
|
206
|
+
if (this.connection) {
|
|
207
|
+
await this.connection.remoteXPC.close();
|
|
208
|
+
this.connection = null;
|
|
209
|
+
}
|
|
147
210
|
}
|
|
148
|
-
|
|
149
|
-
|
|
211
|
+
|
|
212
|
+
isActive(): boolean {
|
|
213
|
+
return this.connection !== null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public async startConnection(): Promise<DVTServiceWithConnection> {
|
|
217
|
+
const Services = await getRemoteXPCServices();
|
|
218
|
+
return Services.startDVTService(this.udid);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Instrument Service-based implementation for iOS < 18
|
|
224
|
+
*/
|
|
225
|
+
class InstrumentConditionInducer implements IConditionInducer {
|
|
226
|
+
private service: any | null = null; // InstrumentService type from appium-ios-device
|
|
227
|
+
|
|
228
|
+
constructor(
|
|
229
|
+
private readonly udid: string,
|
|
230
|
+
private readonly log: AppiumLogger,
|
|
231
|
+
) {}
|
|
232
|
+
|
|
233
|
+
async list(): Promise<Condition[]> {
|
|
234
|
+
const service = await services.startInstrumentService(this.udid);
|
|
235
|
+
try {
|
|
236
|
+
const ret = await service.callChannel(
|
|
237
|
+
INSTRUMENT_CHANNEL.CONDITION_INDUCER,
|
|
238
|
+
'availableConditionInducers',
|
|
239
|
+
);
|
|
240
|
+
return ret.selector;
|
|
241
|
+
} finally {
|
|
242
|
+
service.close();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async enable(conditionID: string, profileID: string): Promise<boolean> {
|
|
247
|
+
if (this.service && !this.service._socketClient.destroyed) {
|
|
248
|
+
throw new Error(`Condition inducer has been started. A condition is already active.`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
this.service = await services.startInstrumentService(this.udid);
|
|
252
|
+
const ret = await this.service.callChannel(
|
|
150
253
|
INSTRUMENT_CHANNEL.CONDITION_INDUCER,
|
|
151
|
-
'
|
|
254
|
+
'enableConditionWithIdentifier:profileIdentifier:',
|
|
255
|
+
conditionID,
|
|
256
|
+
profileID,
|
|
152
257
|
);
|
|
258
|
+
|
|
153
259
|
if (!_.isBoolean(ret.selector)) {
|
|
154
|
-
this.
|
|
155
|
-
|
|
260
|
+
this.service.close();
|
|
261
|
+
this.service = null;
|
|
262
|
+
throw new Error(`Enable condition inducer error: '${JSON.stringify(ret.selector)}'`);
|
|
156
263
|
}
|
|
264
|
+
|
|
157
265
|
return ret.selector;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async disable(): Promise<boolean> {
|
|
269
|
+
if (!this.service) {
|
|
270
|
+
this.log.warn('Condition inducer server has not started');
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const ret = await this.service.callChannel(
|
|
276
|
+
INSTRUMENT_CHANNEL.CONDITION_INDUCER,
|
|
277
|
+
'disableActiveCondition',
|
|
278
|
+
);
|
|
279
|
+
if (!_.isBoolean(ret.selector)) {
|
|
280
|
+
this.log.warn(`Disable condition inducer error: '${JSON.stringify(ret.selector)}'`);
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return ret.selector;
|
|
284
|
+
} finally {
|
|
285
|
+
if (this.service) {
|
|
286
|
+
this.service.close();
|
|
287
|
+
this.service = null;
|
|
288
|
+
}
|
|
162
289
|
}
|
|
163
290
|
}
|
|
164
|
-
}
|
|
165
291
|
|
|
166
|
-
async
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
292
|
+
async close(): Promise<void> {
|
|
293
|
+
if (this.service) {
|
|
294
|
+
this.service.close();
|
|
295
|
+
this.service = null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
170
298
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
await this._remoteXPCConditionInducerConnection.remoteXPC.close();
|
|
174
|
-
this._remoteXPCConditionInducerConnection = null;
|
|
299
|
+
isActive(): boolean {
|
|
300
|
+
return this.service !== null && !this.service._socketClient.destroyed;
|
|
175
301
|
}
|
|
176
302
|
}
|
|
177
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Factory function to create the appropriate condition inducer implementation
|
|
306
|
+
* based on the iOS version
|
|
307
|
+
*/
|
|
308
|
+
async function createConditionInducer(
|
|
309
|
+
driver: XCUITestDriver,
|
|
310
|
+
): Promise<IConditionInducer> {
|
|
311
|
+
if (!isIos18OrNewer(driver.opts)) {
|
|
312
|
+
return new InstrumentConditionInducer(driver.device.udid, driver.log);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const xpcInducer = new RemoteXPCConditionInducer(driver.device.udid, driver.log);
|
|
316
|
+
try {
|
|
317
|
+
const connection = await xpcInducer.startConnection();
|
|
318
|
+
await connection.remoteXPC.close();
|
|
319
|
+
} catch (err: any) {
|
|
320
|
+
driver.log.warn(
|
|
321
|
+
`Unable to use RemoteXPC-based condition inducer for device ${driver.device.udid}, ` +
|
|
322
|
+
`falling back to the legacy implementation: ${err.message}`
|
|
323
|
+
);
|
|
324
|
+
return new InstrumentConditionInducer(driver.device.udid, driver.log);
|
|
325
|
+
}
|
|
326
|
+
return xpcInducer;
|
|
327
|
+
}
|
|
178
328
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {node} from 'appium/support';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {readFileSync} from 'node:fs';
|
|
4
|
+
import type {Services} from 'appium-ios-remotexpc';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cached RemoteXPC Services module
|
|
8
|
+
*/
|
|
9
|
+
let cachedRemoteXPCServices: typeof Services | null = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Module root and version cached at initialization
|
|
13
|
+
*/
|
|
14
|
+
const {moduleRoot, remoteXpcVersion} = fetchInstallInfo();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the RemoteXPC Services module dynamically
|
|
18
|
+
*
|
|
19
|
+
* This helper centralizes the import of appium-ios-remotexpc to:
|
|
20
|
+
* - Provide consistent error handling across all services
|
|
21
|
+
* - Give helpful installation instructions when the module is missing
|
|
22
|
+
*
|
|
23
|
+
* @returns The Services export from appium-ios-remotexpc
|
|
24
|
+
* @throws {Error} If the module cannot be imported
|
|
25
|
+
*/
|
|
26
|
+
export async function getRemoteXPCServices(): Promise<typeof Services> {
|
|
27
|
+
if (cachedRemoteXPCServices) {
|
|
28
|
+
return cachedRemoteXPCServices;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const remotexpcModule = await import('appium-ios-remotexpc');
|
|
33
|
+
cachedRemoteXPCServices = remotexpcModule.Services;
|
|
34
|
+
return cachedRemoteXPCServices;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const error = err as Error;
|
|
37
|
+
|
|
38
|
+
if (error.message.includes('Cannot find module')) {
|
|
39
|
+
let errorMessage =
|
|
40
|
+
'Failed to import appium-ios-remotexpc module. ' +
|
|
41
|
+
'This module is required for iOS 18 and above device operations.';
|
|
42
|
+
|
|
43
|
+
if (moduleRoot && remoteXpcVersion) {
|
|
44
|
+
errorMessage +=
|
|
45
|
+
' Please install it by running: ' +
|
|
46
|
+
`cd "${moduleRoot}" && npm install "appium-ios-remotexpc@${remoteXpcVersion}".`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
errorMessage += ` Original error: ${error.message}`;
|
|
50
|
+
throw new Error(errorMessage);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(
|
|
54
|
+
'Failed to import appium-ios-remotexpc module. ' +
|
|
55
|
+
'This module is required for iOS 18 and above device operations. ' +
|
|
56
|
+
`Original error: ${error.message}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fetch module root and appium-ios-remotexpc version from package.json
|
|
63
|
+
*
|
|
64
|
+
* @returns Object containing moduleRoot and remoteXpcVersion
|
|
65
|
+
*/
|
|
66
|
+
function fetchInstallInfo(): {
|
|
67
|
+
moduleRoot: string | undefined;
|
|
68
|
+
remoteXpcVersion: string | undefined;
|
|
69
|
+
} {
|
|
70
|
+
try {
|
|
71
|
+
const root = node.getModuleRootSync('appium-xcuitest-driver', __filename);
|
|
72
|
+
if (root) {
|
|
73
|
+
const packageJsonPath = path.join(root, 'package.json');
|
|
74
|
+
const packageJsonContent = readFileSync(packageJsonPath, 'utf8');
|
|
75
|
+
if (packageJsonContent) {
|
|
76
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
77
|
+
return {
|
|
78
|
+
moduleRoot: root,
|
|
79
|
+
remoteXpcVersion: packageJson.optionalDependencies?.['appium-ios-remotexpc'],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
// Error messages will skip install hints
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {moduleRoot: undefined, remoteXpcVersion: undefined};
|
|
88
|
+
}
|
package/lib/driver.ts
CHANGED
|
@@ -39,6 +39,7 @@ import * as biometricCommands from './commands/biometric';
|
|
|
39
39
|
import * as certificateCommands from './commands/certificate';
|
|
40
40
|
import * as clipboardCommands from './commands/clipboard';
|
|
41
41
|
import * as conditionCommands from './commands/condition';
|
|
42
|
+
import type {IConditionInducer} from './commands/condition';
|
|
42
43
|
import * as contentSizeCommands from './commands/content-size';
|
|
43
44
|
import * as contextCommands from './commands/context';
|
|
44
45
|
import * as deviceInfoCommands from './commands/device-info';
|
|
@@ -121,7 +122,6 @@ import type { PerfRecorder } from './commands/performance';
|
|
|
121
122
|
import type { AudioRecorder } from './commands/record-audio';
|
|
122
123
|
import type { TrafficCapture } from './commands/pcap';
|
|
123
124
|
import type { ScreenRecorder } from './commands/recordscreen';
|
|
124
|
-
import type { DVTServiceWithConnection } from 'appium-ios-remotexpc';
|
|
125
125
|
import type { IOSDeviceLog } from './device/log/ios-device-log';
|
|
126
126
|
import type { IOSSimulatorLog } from './device/log/ios-simulator-log';
|
|
127
127
|
import type { IOSCrashLog } from './device/log/ios-crash-log';
|
|
@@ -262,8 +262,7 @@ export class XCUITestDriver
|
|
|
262
262
|
_perfRecorders: PerfRecorder[];
|
|
263
263
|
webElementsCache: LRUCache<any, any>;
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
_remoteXPCConditionInducerConnection: DVTServiceWithConnection | null; // RemoteXPC DVT connection for iOS>=18 condition inducer
|
|
265
|
+
_conditionInducer: IConditionInducer | null; // Condition inducer facade that abstracts implementation details
|
|
267
266
|
_isSafariIphone: boolean | undefined;
|
|
268
267
|
_isSafariNotched: boolean | undefined;
|
|
269
268
|
_waitingAtoms: WaitingAtoms;
|
|
@@ -429,7 +428,7 @@ export class XCUITestDriver
|
|
|
429
428
|
this._perfRecorders = [];
|
|
430
429
|
}
|
|
431
430
|
|
|
432
|
-
if (this.
|
|
431
|
+
if (this._conditionInducer) {
|
|
433
432
|
try {
|
|
434
433
|
await this.disableConditionInducer();
|
|
435
434
|
} catch (err) {
|
|
@@ -1743,8 +1742,7 @@ export class XCUITestDriver
|
|
|
1743
1742
|
this.pageLoadMs = 6000;
|
|
1744
1743
|
this.landscapeWebCoordsOffset = 0;
|
|
1745
1744
|
this._remote = null;
|
|
1746
|
-
this.
|
|
1747
|
-
this._remoteXPCConditionInducerConnection = null;
|
|
1745
|
+
this._conditionInducer = null;
|
|
1748
1746
|
|
|
1749
1747
|
this.webElementsCache = new LRUCache({
|
|
1750
1748
|
max: WEB_ELEMENTS_CACHE_SIZE,
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-xcuitest-driver",
|
|
3
|
-
"version": "10.14.
|
|
3
|
+
"version": "10.14.11",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "appium-xcuitest-driver",
|
|
9
|
-
"version": "10.14.
|
|
9
|
+
"version": "10.14.11",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@appium/strongbox": "^1.0.0-rc.1",
|
|
@@ -321,12 +321,12 @@
|
|
|
321
321
|
}
|
|
322
322
|
},
|
|
323
323
|
"node_modules/@babel/code-frame": {
|
|
324
|
-
"version": "7.
|
|
325
|
-
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.
|
|
326
|
-
"integrity": "sha512-
|
|
324
|
+
"version": "7.28.6",
|
|
325
|
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
|
|
326
|
+
"integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
|
|
327
327
|
"license": "MIT",
|
|
328
328
|
"dependencies": {
|
|
329
|
-
"@babel/helper-validator-identifier": "^7.
|
|
329
|
+
"@babel/helper-validator-identifier": "^7.28.5",
|
|
330
330
|
"js-tokens": "^4.0.0",
|
|
331
331
|
"picocolors": "^1.1.1"
|
|
332
332
|
},
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"xcuitest",
|
|
9
9
|
"xctest"
|
|
10
10
|
],
|
|
11
|
-
"version": "10.14.
|
|
11
|
+
"version": "10.14.11",
|
|
12
12
|
"author": "Appium Contributors",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"mainClass": "XCUITestDriver",
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build-wda": "./scripts/build-wda.
|
|
34
|
+
"build-wda": "./scripts/build-wda.mjs",
|
|
35
35
|
"open-wda": "./scripts/open-wda.mjs",
|
|
36
36
|
"tunnel-creation": "./scripts/tunnel-creation.mjs",
|
|
37
37
|
"download-wda-sim": "./scripts/download-wda-sim.mjs",
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import {WebDriverAgent} from 'appium-webdriveragent';
|
|
2
|
+
import * as xcode from 'appium-xcode';
|
|
3
|
+
import {Simctl} from 'node-simctl';
|
|
4
|
+
import {getSimulator} from 'appium-ios-simulator';
|
|
5
|
+
import {logger} from 'appium/support.js';
|
|
6
|
+
import {parseArgValue} from './utils.js';
|
|
7
7
|
|
|
8
8
|
const log = logger.getLogger('WDA');
|
|
9
9
|
|
|
10
10
|
async function build() {
|
|
11
11
|
const customDevice = parseArgValue('name');
|
|
12
|
-
const xcodeVersion = await xcode.getVersion(true);
|
|
13
12
|
const platformVersion = parseArgValue('sdk') || (await xcode.getMaxIOSSDK());
|
|
14
13
|
const iosDevices = await new Simctl().getDevices(platformVersion, 'iOS');
|
|
15
14
|
const verifyDevicePresence = (info) => {
|
|
@@ -27,7 +26,7 @@ async function build() {
|
|
|
27
26
|
platform: deviceInfo.platform,
|
|
28
27
|
checkExistence: false,
|
|
29
28
|
});
|
|
30
|
-
const wda = new WebDriverAgent(
|
|
29
|
+
const wda = new WebDriverAgent({
|
|
31
30
|
iosSdkVersion: platformVersion,
|
|
32
31
|
platformVersion,
|
|
33
32
|
showXcodeLog: true,
|