@midscene/ios 0.30.9 → 0.30.11-beta-20251218071621.0
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/README.md +11 -11
- package/dist/es/bin.mjs +467 -339
- package/dist/es/index.mjs +194 -76
- package/dist/lib/bin.js +437 -295
- package/dist/lib/index.js +199 -81
- package/dist/types/index.d.ts +103 -14
- package/package.json +8 -7
- package/static/index.html +1 -1
- package/static/static/css/index.d32b7df9.css +2 -0
- package/static/static/css/index.d32b7df9.css.map +1 -0
- package/static/static/js/79.25af61dc.js +611 -0
- package/static/static/js/{931.dc961e99.js.LICENSE.txt → 79.25af61dc.js.LICENSE.txt} +0 -4
- package/static/static/js/79.25af61dc.js.map +1 -0
- package/static/static/js/async/195.0366f6e8.js +3 -0
- package/static/static/js/async/195.0366f6e8.js.map +1 -0
- package/static/static/js/async/{702.60261735.js → 199.f31e52e7.js} +20 -20
- package/static/static/js/async/199.f31e52e7.js.map +1 -0
- package/static/static/js/async/221.591b048e.js +21 -0
- package/static/static/js/async/221.591b048e.js.map +1 -0
- package/static/static/js/async/271.15d46ff8.js +30 -0
- package/static/static/js/async/271.15d46ff8.js.map +1 -0
- package/static/static/js/async/35.2b64fb0f.js +1 -0
- package/static/static/js/async/{644.6bdc4065.js → 467.710fa05a.js} +1 -1
- package/static/static/js/async/652.b5a7c7b4.js +3 -0
- package/static/static/js/async/652.b5a7c7b4.js.map +1 -0
- package/static/static/js/async/856.be9fd814.js +158 -0
- package/static/static/js/async/{212.e243c338.js.map → 856.be9fd814.js.map} +1 -1
- package/static/static/js/async/860.b56301d9.js +2 -0
- package/static/static/js/async/860.b56301d9.js.map +1 -0
- package/static/static/js/async/990.82a78a53.js +26 -0
- package/static/static/js/async/990.82a78a53.js.map +1 -0
- package/static/static/js/index.0bbfffbf.js +10 -0
- package/static/static/js/index.0bbfffbf.js.map +1 -0
- package/static/static/js/lib-react.7b1abe58.js +3 -0
- package/static/static/js/lib-react.7b1abe58.js.map +1 -0
- package/static/static/css/index.44466eb4.css +0 -2
- package/static/static/css/index.44466eb4.css.map +0 -1
- package/static/static/js/931.dc961e99.js +0 -620
- package/static/static/js/931.dc961e99.js.map +0 -1
- package/static/static/js/async/173.9cf6b074.js +0 -3
- package/static/static/js/async/173.9cf6b074.js.map +0 -1
- package/static/static/js/async/212.e243c338.js +0 -158
- package/static/static/js/async/329.f888b505.js +0 -26
- package/static/static/js/async/329.f888b505.js.map +0 -1
- package/static/static/js/async/364.1821e74b.js +0 -30
- package/static/static/js/async/364.1821e74b.js.map +0 -1
- package/static/static/js/async/544.b73fa603.js +0 -2
- package/static/static/js/async/544.b73fa603.js.map +0 -1
- package/static/static/js/async/582.5dccae2d.js +0 -21
- package/static/static/js/async/582.5dccae2d.js.map +0 -1
- package/static/static/js/async/624.45ee2b2c.js +0 -3
- package/static/static/js/async/624.45ee2b2c.js.map +0 -1
- package/static/static/js/async/659.9afd03db.js +0 -21
- package/static/static/js/async/659.9afd03db.js.map +0 -1
- package/static/static/js/async/702.60261735.js.map +0 -1
- package/static/static/js/async/920.7d9a9aa8.js +0 -2
- package/static/static/js/async/920.7d9a9aa8.js.map +0 -1
- package/static/static/js/async/983.8b91303f.js +0 -1
- package/static/static/js/index.c7ba8adb.js +0 -10
- package/static/static/js/index.c7ba8adb.js.map +0 -1
- package/static/static/js/lib-react.f566a9ed.js +0 -3
- package/static/static/js/lib-react.f566a9ed.js.map +0 -1
- /package/static/static/js/{index.c7ba8adb.js.LICENSE.txt → index.0bbfffbf.js.LICENSE.txt} +0 -0
- /package/static/static/js/{lib-react.f566a9ed.js.LICENSE.txt → lib-react.7b1abe58.js.LICENSE.txt} +0 -0
- /package/static/static/wasm/{9e906fbf55e08f98.module.wasm → 9e906fbf.module.wasm} +0 -0
package/dist/es/index.mjs
CHANGED
|
@@ -150,6 +150,48 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
150
150
|
debugIOS(`Warning: Key "${key}" is not supported on iOS platform`);
|
|
151
151
|
throw new Error(`Key "${key}" is not supported on iOS platform`);
|
|
152
152
|
}
|
|
153
|
+
async getActiveElement() {
|
|
154
|
+
this.ensureSession();
|
|
155
|
+
debugIOS('Getting active element');
|
|
156
|
+
try {
|
|
157
|
+
const response = await this.makeRequest('GET', `/session/${this.sessionId}/element/active`);
|
|
158
|
+
const elementId = response.value?.ELEMENT || response.value?.['element-6066-11e4-a52e-4f735466cecf'] || response.ELEMENT || response['element-6066-11e4-a52e-4f735466cecf'];
|
|
159
|
+
if (elementId) {
|
|
160
|
+
debugIOS(`Got active element ID: ${elementId}`);
|
|
161
|
+
return elementId;
|
|
162
|
+
}
|
|
163
|
+
debugIOS('No active element found');
|
|
164
|
+
return null;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
debugIOS(`Failed to get active element: ${error}`);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async clearElement(elementId) {
|
|
171
|
+
this.ensureSession();
|
|
172
|
+
debugIOS(`Clearing element: ${elementId}`);
|
|
173
|
+
try {
|
|
174
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/element/${elementId}/clear`);
|
|
175
|
+
debugIOS('Element cleared successfully');
|
|
176
|
+
} catch (error) {
|
|
177
|
+
debugIOS(`Failed to clear element: ${error}`);
|
|
178
|
+
throw new Error(`Failed to clear element: ${error}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async clearActiveElement() {
|
|
182
|
+
try {
|
|
183
|
+
const elementId = await this.getActiveElement();
|
|
184
|
+
if (!elementId) {
|
|
185
|
+
debugIOS('No active element to clear');
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
await this.clearElement(elementId);
|
|
189
|
+
return true;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
debugIOS(`Failed to clear active element: ${error}`);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
153
195
|
normalizeKeyName(key) {
|
|
154
196
|
return key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
|
|
155
197
|
}
|
|
@@ -190,8 +232,17 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
190
232
|
});
|
|
191
233
|
debugIOS(`Tapped at coordinates (${x}, ${y})`);
|
|
192
234
|
} catch (error) {
|
|
193
|
-
debugIOS(`
|
|
194
|
-
|
|
235
|
+
debugIOS(`New tap endpoint failed, trying legacy endpoint: ${error}`);
|
|
236
|
+
try {
|
|
237
|
+
await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap/0`, {
|
|
238
|
+
x,
|
|
239
|
+
y
|
|
240
|
+
});
|
|
241
|
+
debugIOS(`Tapped at coordinates (${x}, ${y}) using legacy endpoint`);
|
|
242
|
+
} catch (fallbackError) {
|
|
243
|
+
debugIOS(`Failed to tap at (${x}, ${y}): ${fallbackError}`);
|
|
244
|
+
throw new Error(`Failed to tap at coordinates: ${fallbackError}`);
|
|
245
|
+
}
|
|
195
246
|
}
|
|
196
247
|
}
|
|
197
248
|
async swipe(fromX, fromY, toX, toY, duration = 500) {
|
|
@@ -264,13 +315,34 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
264
315
|
debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
|
|
265
316
|
}
|
|
266
317
|
async getScreenScale() {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
318
|
+
this.ensureSession();
|
|
319
|
+
try {
|
|
320
|
+
const screenResponse = await this.makeRequest('GET', `/session/${this.sessionId}/wda/screen`);
|
|
321
|
+
if (screenResponse?.value?.scale) {
|
|
322
|
+
debugIOS(`Got screen scale from WDA screen endpoint: ${screenResponse.value.scale}`);
|
|
323
|
+
return screenResponse.value.scale;
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
debugIOS(`Failed to get screen scale from /wda/screen: ${error}`);
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
debugIOS('Calculating screen scale from screenshot and window size');
|
|
330
|
+
const [screenshotBase64, windowSize] = await Promise.all([
|
|
331
|
+
this.takeScreenshot(),
|
|
332
|
+
this.getWindowSize()
|
|
333
|
+
]);
|
|
334
|
+
const { jimpFromBase64 } = await import("@midscene/shared/img");
|
|
335
|
+
const screenshotImg = await jimpFromBase64(screenshotBase64);
|
|
336
|
+
const screenshotWidth = screenshotImg.bitmap.width;
|
|
337
|
+
const screenshotHeight = screenshotImg.bitmap.height;
|
|
338
|
+
const scale = Math.max(screenshotWidth, screenshotHeight) / Math.max(windowSize.width, windowSize.height);
|
|
339
|
+
const roundedScale = Math.round(scale);
|
|
340
|
+
debugIOS(`Calculated screen scale: ${roundedScale} (screenshot: ${screenshotWidth}x${screenshotHeight}, window: ${windowSize.width}x${windowSize.height})`);
|
|
341
|
+
return roundedScale;
|
|
342
|
+
} catch (error) {
|
|
343
|
+
debugIOS(`Failed to calculate screen scale: ${error}`);
|
|
272
344
|
}
|
|
273
|
-
debugIOS('No screen scale found
|
|
345
|
+
debugIOS('No screen scale found');
|
|
274
346
|
return null;
|
|
275
347
|
}
|
|
276
348
|
async createSession(capabilities) {
|
|
@@ -297,6 +369,10 @@ class IOSWebDriverClient extends WebDriverClient {
|
|
|
297
369
|
debugIOS(`Failed to apply iOS session configuration: ${error}`);
|
|
298
370
|
}
|
|
299
371
|
}
|
|
372
|
+
async executeRequest(method, endpoint, data) {
|
|
373
|
+
this.ensureSession();
|
|
374
|
+
return this.makeRequest(method, endpoint, data);
|
|
375
|
+
}
|
|
300
376
|
}
|
|
301
377
|
function _define_property(obj, key, value) {
|
|
302
378
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -309,7 +385,12 @@ function _define_property(obj, key, value) {
|
|
|
309
385
|
return obj;
|
|
310
386
|
}
|
|
311
387
|
const debugDevice = getDebug('ios:device');
|
|
312
|
-
const
|
|
388
|
+
const WDA_HTTP_METHODS = [
|
|
389
|
+
'GET',
|
|
390
|
+
'POST',
|
|
391
|
+
'DELETE',
|
|
392
|
+
'PUT'
|
|
393
|
+
];
|
|
313
394
|
class IOSDevice {
|
|
314
395
|
actionSpace() {
|
|
315
396
|
const defaultActions = [
|
|
@@ -329,7 +410,7 @@ class IOSDevice {
|
|
|
329
410
|
interfaceAlias: 'aiInput',
|
|
330
411
|
paramSchema: z.object({
|
|
331
412
|
value: z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
|
|
332
|
-
autoDismissKeyboard: z.boolean().optional().describe('
|
|
413
|
+
autoDismissKeyboard: z.boolean().optional().describe('Whether to dismiss the keyboard after input. Defaults to true if not specified. Set to false to keep the keyboard visible after input.'),
|
|
333
414
|
mode: z["enum"]([
|
|
334
415
|
'replace',
|
|
335
416
|
'clear',
|
|
@@ -338,14 +419,13 @@ class IOSDevice {
|
|
|
338
419
|
locate: getMidsceneLocationSchema().describe('The input field to be filled').optional()
|
|
339
420
|
}),
|
|
340
421
|
call: async (param)=>{
|
|
341
|
-
var _this_options;
|
|
342
422
|
const element = param.locate;
|
|
343
423
|
if (element) {
|
|
344
424
|
if ('append' !== param.mode) await this.clearInput(element);
|
|
345
425
|
}
|
|
346
426
|
if ('clear' === param.mode) return;
|
|
347
427
|
if (!param || !param.value) return;
|
|
348
|
-
const autoDismissKeyboard = param.autoDismissKeyboard ??
|
|
428
|
+
const autoDismissKeyboard = param.autoDismissKeyboard ?? this.options?.autoDismissKeyboard;
|
|
349
429
|
await this.typeText(param.value, {
|
|
350
430
|
autoDismissKeyboard
|
|
351
431
|
});
|
|
@@ -357,18 +437,18 @@ class IOSDevice {
|
|
|
357
437
|
left: element.center[0],
|
|
358
438
|
top: element.center[1]
|
|
359
439
|
} : void 0;
|
|
360
|
-
const scrollToEventName =
|
|
361
|
-
if ('
|
|
362
|
-
else if ('
|
|
363
|
-
else if ('
|
|
364
|
-
else if ('
|
|
365
|
-
else if ('
|
|
440
|
+
const scrollToEventName = param?.scrollType;
|
|
441
|
+
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
442
|
+
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
443
|
+
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
444
|
+
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
445
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
366
446
|
else {
|
|
367
|
-
if (
|
|
447
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
|
|
368
448
|
else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
|
|
369
449
|
else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
|
|
370
450
|
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
371
|
-
else await this.scrollDown(
|
|
451
|
+
else await this.scrollDown(param?.distance || void 0, startingPoint);
|
|
372
452
|
await sleep(500);
|
|
373
453
|
}
|
|
374
454
|
}),
|
|
@@ -382,22 +462,6 @@ class IOSDevice {
|
|
|
382
462
|
defineActionKeyboardPress(async (param)=>{
|
|
383
463
|
await this.pressKey(param.keyName);
|
|
384
464
|
}),
|
|
385
|
-
defineAction({
|
|
386
|
-
name: 'IOSHomeButton',
|
|
387
|
-
description: 'Trigger the system "home" operation on iOS devices',
|
|
388
|
-
paramSchema: z.object({}),
|
|
389
|
-
call: async ()=>{
|
|
390
|
-
await this.home();
|
|
391
|
-
}
|
|
392
|
-
}),
|
|
393
|
-
defineAction({
|
|
394
|
-
name: 'IOSAppSwitcher',
|
|
395
|
-
description: 'Trigger the system "app switcher" operation on iOS devices',
|
|
396
|
-
paramSchema: z.object({}),
|
|
397
|
-
call: async ()=>{
|
|
398
|
-
await this.appSwitcher();
|
|
399
|
-
}
|
|
400
|
-
}),
|
|
401
465
|
defineAction({
|
|
402
466
|
name: 'IOSLongPress',
|
|
403
467
|
description: 'Trigger a long press on the screen at specified coordinates on iOS devices',
|
|
@@ -409,7 +473,7 @@ class IOSDevice {
|
|
|
409
473
|
const element = param.locate;
|
|
410
474
|
node_assert(element, 'IOSLongPress requires an element to be located');
|
|
411
475
|
const [x, y] = element.center;
|
|
412
|
-
await this.longPress(x, y,
|
|
476
|
+
await this.longPress(x, y, param?.duration);
|
|
413
477
|
}
|
|
414
478
|
}),
|
|
415
479
|
defineActionClearInput(async (param)=>{
|
|
@@ -418,9 +482,11 @@ class IOSDevice {
|
|
|
418
482
|
await this.clearInput(element);
|
|
419
483
|
})
|
|
420
484
|
];
|
|
485
|
+
const platformSpecificActions = Object.values(createPlatformActions(this));
|
|
421
486
|
const customActions = this.customActions || [];
|
|
422
487
|
return [
|
|
423
488
|
...defaultActions,
|
|
489
|
+
...platformSpecificActions,
|
|
424
490
|
...customActions
|
|
425
491
|
];
|
|
426
492
|
}
|
|
@@ -437,7 +503,7 @@ class IOSDevice {
|
|
|
437
503
|
await this.wdaManager.start();
|
|
438
504
|
await this.wdaBackend.createSession();
|
|
439
505
|
const deviceInfo = await this.wdaBackend.getDeviceInfo();
|
|
440
|
-
if (
|
|
506
|
+
if (deviceInfo?.udid) {
|
|
441
507
|
this.deviceId = deviceInfo.udid;
|
|
442
508
|
debugDevice(`Updated device ID to real UDID: ${this.deviceId}`);
|
|
443
509
|
}
|
|
@@ -518,19 +584,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
518
584
|
if (!element) return;
|
|
519
585
|
await this.tap(element.center[0], element.center[1]);
|
|
520
586
|
await sleep(100);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
await this.wdaBackend.typeText(BackspaceChar);
|
|
525
|
-
} catch (error2) {
|
|
526
|
-
debugDevice(`Method 1 failed, trying method 2: ${error2}`);
|
|
527
|
-
try {
|
|
528
|
-
const backspaces = Array(100).fill(BackspaceChar).join('');
|
|
529
|
-
await this.wdaBackend.typeText(backspaces);
|
|
530
|
-
} catch (error3) {
|
|
531
|
-
debugDevice(`All clear methods failed: ${error3}`);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
587
|
+
debugDevice('Attempting to clear input with WebDriver Clear API');
|
|
588
|
+
const cleared = await this.wdaBackend.clearActiveElement();
|
|
589
|
+
cleared ? debugDevice('Successfully cleared input with WebDriver Clear API') : debugDevice('WebDriver Clear API returned false (no active element or clear failed)');
|
|
534
590
|
}
|
|
535
591
|
async url() {
|
|
536
592
|
return '';
|
|
@@ -555,9 +611,8 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
555
611
|
await this.wdaBackend.swipe(Math.round(fromX), Math.round(fromY), Math.round(toX), Math.round(toY), duration);
|
|
556
612
|
}
|
|
557
613
|
async typeText(text, options) {
|
|
558
|
-
var _this_options;
|
|
559
614
|
if (!text) return;
|
|
560
|
-
const shouldAutoDismissKeyboard =
|
|
615
|
+
const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
|
|
561
616
|
debugDevice(`Typing text: "${text}"`);
|
|
562
617
|
try {
|
|
563
618
|
await sleep(200);
|
|
@@ -759,20 +814,30 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
759
814
|
}
|
|
760
815
|
async hideKeyboard(keyNames) {
|
|
761
816
|
try {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
817
|
+
const dismissKeys = keyNames && keyNames.length > 0 ? keyNames : [
|
|
818
|
+
'return',
|
|
819
|
+
'done',
|
|
820
|
+
'go',
|
|
821
|
+
'search',
|
|
822
|
+
'next',
|
|
823
|
+
'send'
|
|
824
|
+
];
|
|
825
|
+
debugDevice(`Attempting to dismiss keyboard using WDA API with keys: ${dismissKeys.join(', ')}`);
|
|
826
|
+
try {
|
|
827
|
+
await this.wdaBackend.dismissKeyboard(dismissKeys);
|
|
828
|
+
debugDevice('Successfully dismissed keyboard using WDA API');
|
|
829
|
+
await sleep(500);
|
|
767
830
|
return true;
|
|
831
|
+
} catch (wdaError) {
|
|
832
|
+
debugDevice(`WDA dismissKeyboard failed, falling back to swipe gesture: ${wdaError}`);
|
|
768
833
|
}
|
|
769
834
|
const windowSize = await this.wdaBackend.getWindowSize();
|
|
770
835
|
const centerX = Math.round(windowSize.width / 2);
|
|
771
|
-
const startY = Math.round(0.
|
|
772
|
-
const endY = Math.round(0.
|
|
773
|
-
await this.swipe(centerX, startY, centerX, endY,
|
|
774
|
-
debugDevice('Dismissed keyboard with swipe
|
|
775
|
-
await sleep(
|
|
836
|
+
const startY = Math.round(0.9 * windowSize.height);
|
|
837
|
+
const endY = Math.round(0.5 * windowSize.height);
|
|
838
|
+
await this.swipe(centerX, startY, centerX, endY, 300);
|
|
839
|
+
debugDevice('Dismissed keyboard with swipe up gesture from bottom of screen');
|
|
840
|
+
await sleep(500);
|
|
776
841
|
return true;
|
|
777
842
|
} catch (error) {
|
|
778
843
|
debugDevice(`Failed to hide keyboard: ${error}`);
|
|
@@ -801,6 +866,7 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
801
866
|
async openUrlViaSafari(url) {
|
|
802
867
|
try {
|
|
803
868
|
debugDevice(`Opening URL via Safari: ${url}`);
|
|
869
|
+
await this.wdaBackend.terminateApp('com.apple.mobilesafari');
|
|
804
870
|
await this.wdaBackend.launchApp('com.apple.mobilesafari');
|
|
805
871
|
await sleep(2000);
|
|
806
872
|
await this.typeText(url);
|
|
@@ -818,6 +884,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
818
884
|
throw new Error(`Failed to open URL via Safari: ${error}`);
|
|
819
885
|
}
|
|
820
886
|
}
|
|
887
|
+
async runWdaRequest(method, endpoint, data) {
|
|
888
|
+
return await this.wdaBackend.executeRequest(method, endpoint, data);
|
|
889
|
+
}
|
|
821
890
|
async destroy() {
|
|
822
891
|
if (this.destroyed) return;
|
|
823
892
|
try {
|
|
@@ -843,9 +912,9 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
843
912
|
_define_property(this, "options", void 0);
|
|
844
913
|
this.deviceId = 'pending-connection';
|
|
845
914
|
this.options = options;
|
|
846
|
-
this.customActions =
|
|
847
|
-
const wdaPort =
|
|
848
|
-
const wdaHost =
|
|
915
|
+
this.customActions = options?.customActions;
|
|
916
|
+
const wdaPort = options?.wdaPort || DEFAULT_WDA_PORT;
|
|
917
|
+
const wdaHost = options?.wdaHost || 'localhost';
|
|
849
918
|
this.wdaBackend = new IOSWebDriverClient({
|
|
850
919
|
port: wdaPort,
|
|
851
920
|
host: wdaHost
|
|
@@ -853,6 +922,44 @@ ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
|
|
|
853
922
|
this.wdaManager = WDAManager.getInstance(wdaPort, wdaHost);
|
|
854
923
|
}
|
|
855
924
|
}
|
|
925
|
+
const runWdaRequestParamSchema = z.object({
|
|
926
|
+
method: z["enum"](WDA_HTTP_METHODS).describe('HTTP method (GET, POST, DELETE, PUT)'),
|
|
927
|
+
endpoint: z.string().describe('WebDriver API endpoint'),
|
|
928
|
+
data: z.object({}).passthrough().optional().describe('Optional request body data as JSON object')
|
|
929
|
+
});
|
|
930
|
+
const launchParamSchema = z.string().describe('App bundle ID or URL to launch');
|
|
931
|
+
const createPlatformActions = (device)=>({
|
|
932
|
+
RunWdaRequest: defineAction({
|
|
933
|
+
name: 'RunWdaRequest',
|
|
934
|
+
description: 'Execute WebDriverAgent API request directly on iOS device',
|
|
935
|
+
interfaceAlias: 'runWdaRequest',
|
|
936
|
+
paramSchema: runWdaRequestParamSchema,
|
|
937
|
+
call: async (param)=>await device.runWdaRequest(param.method, param.endpoint, param.data)
|
|
938
|
+
}),
|
|
939
|
+
Launch: defineAction({
|
|
940
|
+
name: 'Launch',
|
|
941
|
+
description: 'Launch an iOS app or URL',
|
|
942
|
+
interfaceAlias: 'launch',
|
|
943
|
+
paramSchema: launchParamSchema,
|
|
944
|
+
call: async (param)=>{
|
|
945
|
+
await device.launch(param);
|
|
946
|
+
}
|
|
947
|
+
}),
|
|
948
|
+
IOSHomeButton: defineAction({
|
|
949
|
+
name: 'IOSHomeButton',
|
|
950
|
+
description: 'Trigger the system "home" operation on iOS devices',
|
|
951
|
+
call: async ()=>{
|
|
952
|
+
await device.home();
|
|
953
|
+
}
|
|
954
|
+
}),
|
|
955
|
+
IOSAppSwitcher: defineAction({
|
|
956
|
+
name: 'IOSAppSwitcher',
|
|
957
|
+
description: 'Trigger the system "app switcher" operation on iOS devices',
|
|
958
|
+
call: async ()=>{
|
|
959
|
+
await device.appSwitcher();
|
|
960
|
+
}
|
|
961
|
+
})
|
|
962
|
+
});
|
|
856
963
|
const execAsync = promisify(exec);
|
|
857
964
|
const debugUtils = getDebug('ios:utils');
|
|
858
965
|
function checkMacOSPlatform() {
|
|
@@ -899,24 +1006,35 @@ async function checkIOSEnvironment() {
|
|
|
899
1006
|
};
|
|
900
1007
|
}
|
|
901
1008
|
}
|
|
1009
|
+
function agent_define_property(obj, key, value) {
|
|
1010
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
1011
|
+
value: value,
|
|
1012
|
+
enumerable: true,
|
|
1013
|
+
configurable: true,
|
|
1014
|
+
writable: true
|
|
1015
|
+
});
|
|
1016
|
+
else obj[key] = value;
|
|
1017
|
+
return obj;
|
|
1018
|
+
}
|
|
902
1019
|
const debugAgent = getDebug('ios:agent');
|
|
903
1020
|
class IOSAgent extends Agent {
|
|
904
|
-
|
|
905
|
-
const
|
|
906
|
-
|
|
1021
|
+
createActionWrapper(name) {
|
|
1022
|
+
const action = this.wrapActionInActionSpace(name);
|
|
1023
|
+
return (...args)=>action(args[0]);
|
|
1024
|
+
}
|
|
1025
|
+
constructor(device, opts){
|
|
1026
|
+
super(device, opts), agent_define_property(this, "launch", void 0), agent_define_property(this, "runWdaRequest", void 0), agent_define_property(this, "home", void 0), agent_define_property(this, "appSwitcher", void 0);
|
|
1027
|
+
this.launch = this.createActionWrapper('Launch');
|
|
1028
|
+
this.runWdaRequest = this.createActionWrapper('RunWdaRequest');
|
|
1029
|
+
this.home = this.createActionWrapper('IOSHomeButton');
|
|
1030
|
+
this.appSwitcher = this.createActionWrapper('IOSAppSwitcher');
|
|
907
1031
|
}
|
|
908
1032
|
}
|
|
909
1033
|
async function agentFromWebDriverAgent(opts) {
|
|
910
1034
|
debugAgent('Creating iOS agent with WebDriverAgent auto-detection');
|
|
911
1035
|
const envCheck = await checkIOSEnvironment();
|
|
912
1036
|
if (!envCheck.available) throw new Error(`iOS environment not available: ${envCheck.error}`);
|
|
913
|
-
const device = new IOSDevice({
|
|
914
|
-
autoDismissKeyboard: null == opts ? void 0 : opts.autoDismissKeyboard,
|
|
915
|
-
customActions: null == opts ? void 0 : opts.customActions,
|
|
916
|
-
wdaPort: null == opts ? void 0 : opts.wdaPort,
|
|
917
|
-
wdaHost: null == opts ? void 0 : opts.wdaHost,
|
|
918
|
-
useWDA: null == opts ? void 0 : opts.useWDA
|
|
919
|
-
});
|
|
1037
|
+
const device = new IOSDevice(opts || {});
|
|
920
1038
|
await device.connect();
|
|
921
1039
|
return new IOSAgent(device, opts);
|
|
922
1040
|
}
|