@midscene/ios 1.0.1-beta-20251201114631.0 → 1.0.1-beta-20251202152706.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/dist/lib/index.js CHANGED
@@ -1,21 +1,5 @@
1
1
  "use strict";
2
- var __webpack_modules__ = {
3
- "@midscene/shared/img": function(module) {
4
- module.exports = import("@midscene/shared/img").then(function(module) {
5
- return module;
6
- });
7
- }
8
- };
9
- var __webpack_module_cache__ = {};
10
- function __webpack_require__(moduleId) {
11
- var cachedModule = __webpack_module_cache__[moduleId];
12
- if (void 0 !== cachedModule) return cachedModule.exports;
13
- var module = __webpack_module_cache__[moduleId] = {
14
- exports: {}
15
- };
16
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
17
- return module.exports;
18
- }
2
+ var __webpack_require__ = {};
19
3
  (()=>{
20
4
  __webpack_require__.n = (module)=>{
21
5
  var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
@@ -47,1062 +31,1056 @@ function __webpack_require__(moduleId) {
47
31
  };
48
32
  })();
49
33
  var __webpack_exports__ = {};
50
- (()=>{
51
- __webpack_require__.r(__webpack_exports__);
52
- __webpack_require__.d(__webpack_exports__, {
53
- overrideAIConfig: ()=>env_namespaceObject.overrideAIConfig,
54
- IOSDevice: ()=>IOSDevice,
55
- IOSWebDriverClient: ()=>IOSWebDriverClient,
56
- agentFromWebDriverAgent: ()=>agentFromWebDriverAgent,
57
- IOSAgent: ()=>IOSAgent,
58
- checkIOSEnvironment: ()=>checkIOSEnvironment
59
- });
60
- const external_node_assert_namespaceObject = require("node:assert");
61
- var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
62
- const core_namespaceObject = require("@midscene/core");
63
- const device_namespaceObject = require("@midscene/core/device");
64
- const utils_namespaceObject = require("@midscene/core/utils");
65
- const constants_namespaceObject = require("@midscene/shared/constants");
66
- const img_namespaceObject = require("@midscene/shared/img");
67
- const logger_namespaceObject = require("@midscene/shared/logger");
68
- const webdriver_namespaceObject = require("@midscene/webdriver");
69
- const debugIOS = (0, logger_namespaceObject.getDebug)('webdriver:ios');
70
- class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
71
- async launchApp(bundleId) {
72
- this.ensureSession();
73
- try {
74
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/launch`, {
75
- bundleId
76
- });
77
- debugIOS(`Launched app: ${bundleId}`);
78
- } catch (error) {
79
- debugIOS(`Failed to launch app ${bundleId}: ${error}`);
80
- throw error;
81
- }
82
- }
83
- async activateApp(bundleId) {
84
- this.ensureSession();
85
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/activate`, {
34
+ __webpack_require__.r(__webpack_exports__);
35
+ __webpack_require__.d(__webpack_exports__, {
36
+ overrideAIConfig: ()=>env_namespaceObject.overrideAIConfig,
37
+ IOSAgent: ()=>IOSAgent,
38
+ checkIOSEnvironment: ()=>checkIOSEnvironment,
39
+ IOSDevice: ()=>IOSDevice,
40
+ IOSWebDriverClient: ()=>IOSWebDriverClient,
41
+ agentFromWebDriverAgent: ()=>agentFromWebDriverAgent
42
+ });
43
+ const external_node_assert_namespaceObject = require("node:assert");
44
+ var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
45
+ const core_namespaceObject = require("@midscene/core");
46
+ const device_namespaceObject = require("@midscene/core/device");
47
+ const utils_namespaceObject = require("@midscene/core/utils");
48
+ const constants_namespaceObject = require("@midscene/shared/constants");
49
+ const img_namespaceObject = require("@midscene/shared/img");
50
+ const logger_namespaceObject = require("@midscene/shared/logger");
51
+ const webdriver_namespaceObject = require("@midscene/webdriver");
52
+ const debugIOS = (0, logger_namespaceObject.getDebug)('webdriver:ios');
53
+ class IOSWebDriverClient extends webdriver_namespaceObject.WebDriverClient {
54
+ async launchApp(bundleId) {
55
+ this.ensureSession();
56
+ try {
57
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/launch`, {
86
58
  bundleId
87
59
  });
60
+ debugIOS(`Launched app: ${bundleId}`);
61
+ } catch (error) {
62
+ debugIOS(`Failed to launch app ${bundleId}: ${error}`);
63
+ throw error;
88
64
  }
89
- async terminateApp(bundleId) {
90
- this.ensureSession();
91
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/terminate`, {
92
- bundleId
65
+ }
66
+ async activateApp(bundleId) {
67
+ this.ensureSession();
68
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/activate`, {
69
+ bundleId
70
+ });
71
+ }
72
+ async terminateApp(bundleId) {
73
+ this.ensureSession();
74
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/apps/terminate`, {
75
+ bundleId
76
+ });
77
+ }
78
+ async openUrl(url) {
79
+ this.ensureSession();
80
+ try {
81
+ await this.makeRequest('POST', `/session/${this.sessionId}/url`, {
82
+ url
83
+ });
84
+ } catch (error) {
85
+ debugIOS(`Direct URL opening failed, trying Safari fallback: ${error}`);
86
+ await this.launchApp('com.apple.mobilesafari');
87
+ await new Promise((resolve)=>setTimeout(resolve, 2000));
88
+ await this.makeRequest('POST', `/session/${this.sessionId}/url`, {
89
+ url
93
90
  });
94
91
  }
95
- async openUrl(url) {
96
- this.ensureSession();
97
- try {
98
- await this.makeRequest('POST', `/session/${this.sessionId}/url`, {
99
- url
100
- });
101
- } catch (error) {
102
- debugIOS(`Direct URL opening failed, trying Safari fallback: ${error}`);
103
- await this.launchApp('com.apple.mobilesafari');
104
- await new Promise((resolve)=>setTimeout(resolve, 2000));
105
- await this.makeRequest('POST', `/session/${this.sessionId}/url`, {
106
- url
107
- });
108
- }
109
- }
110
- async pressHomeButton() {
111
- this.ensureSession();
112
- try {
113
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/pressButton`, {
114
- name: 'home'
115
- });
116
- debugIOS('Home button pressed using hardware key');
117
- } catch (error) {
118
- debugIOS(`Failed to press home button: ${error}`);
119
- throw new Error(`Failed to press home button: ${error}`);
120
- }
92
+ }
93
+ async pressHomeButton() {
94
+ this.ensureSession();
95
+ try {
96
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/pressButton`, {
97
+ name: 'home'
98
+ });
99
+ debugIOS('Home button pressed using hardware key');
100
+ } catch (error) {
101
+ debugIOS(`Failed to press home button: ${error}`);
102
+ throw new Error(`Failed to press home button: ${error}`);
121
103
  }
122
- async appSwitcher() {
123
- this.ensureSession();
124
- try {
125
- const windowSize = await this.getWindowSize();
126
- debugIOS('Triggering app switcher with slow swipe up gesture');
127
- const centerX = windowSize.width / 2;
128
- const startY = windowSize.height - 5;
129
- const endY = 0.5 * windowSize.height;
130
- await this.swipe(centerX, startY, centerX, endY, 1500);
131
- await new Promise((resolve)=>setTimeout(resolve, 800));
132
- } catch (error) {
133
- debugIOS(`App switcher failed: ${error}`);
134
- throw new Error(`Failed to trigger app switcher: ${error}`);
135
- }
104
+ }
105
+ async appSwitcher() {
106
+ this.ensureSession();
107
+ try {
108
+ const windowSize = await this.getWindowSize();
109
+ debugIOS('Triggering app switcher with slow swipe up gesture');
110
+ const centerX = windowSize.width / 2;
111
+ const startY = windowSize.height - 5;
112
+ const endY = 0.5 * windowSize.height;
113
+ await this.swipe(centerX, startY, centerX, endY, 1500);
114
+ await new Promise((resolve)=>setTimeout(resolve, 800));
115
+ } catch (error) {
116
+ debugIOS(`App switcher failed: ${error}`);
117
+ throw new Error(`Failed to trigger app switcher: ${error}`);
136
118
  }
137
- async pressKey(key) {
138
- this.ensureSession();
139
- debugIOS(`Attempting to press key: ${key}`);
140
- if ('Enter' === key || 'Return' === key || 'return' === key) {
141
- debugIOS('Handling Enter/Return key for iOS');
142
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
143
- value: [
144
- '\n'
145
- ]
146
- });
147
- debugIOS('Sent newline character for Enter key');
148
- await new Promise((resolve)=>setTimeout(resolve, 100));
149
- return;
150
- }
151
- if ('Backspace' === key || 'Delete' === key) try {
152
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
153
- value: [
154
- '\b'
155
- ]
156
- });
157
- debugIOS('Sent backspace character');
158
- return;
159
- } catch (error) {
160
- debugIOS(`Backspace failed: ${error}`);
161
- }
162
- if ('Space' === key) try {
163
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
164
- value: [
165
- ' '
166
- ]
167
- });
168
- debugIOS('Sent space character');
169
- return;
170
- } catch (error) {
171
- debugIOS(`Space key failed: ${error}`);
172
- }
173
- const normalizedKey = this.normalizeKeyName(key);
174
- const iosKeyMap = {
175
- Tab: '\t',
176
- ArrowUp: '\uE013',
177
- ArrowDown: '\uE015',
178
- ArrowLeft: '\uE012',
179
- ArrowRight: '\uE014',
180
- Home: '\uE011',
181
- End: '\uE010'
182
- };
183
- if (iosKeyMap[normalizedKey]) try {
184
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
185
- value: [
186
- iosKeyMap[normalizedKey]
187
- ]
188
- });
189
- debugIOS(`Sent WebDriver key code for: ${key}`);
190
- return;
191
- } catch (error) {
192
- debugIOS(`WebDriver key failed for "${key}": ${error}`);
193
- }
194
- if (1 === key.length) try {
195
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
196
- value: [
197
- key
198
- ]
199
- });
200
- debugIOS(`Sent single character: "${key}"`);
201
- return;
202
- } catch (error) {
203
- debugIOS(`Failed to send character "${key}": ${error}`);
204
- }
205
- debugIOS(`Warning: Key "${key}" is not supported on iOS platform`);
206
- throw new Error(`Key "${key}" is not supported on iOS platform`);
119
+ }
120
+ async pressKey(key) {
121
+ this.ensureSession();
122
+ debugIOS(`Attempting to press key: ${key}`);
123
+ if ('Enter' === key || 'Return' === key || 'return' === key) {
124
+ debugIOS('Handling Enter/Return key for iOS');
125
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
126
+ value: [
127
+ '\n'
128
+ ]
129
+ });
130
+ debugIOS('Sent newline character for Enter key');
131
+ await new Promise((resolve)=>setTimeout(resolve, 100));
132
+ return;
133
+ }
134
+ if ('Backspace' === key || 'Delete' === key) try {
135
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
136
+ value: [
137
+ '\b'
138
+ ]
139
+ });
140
+ debugIOS('Sent backspace character');
141
+ return;
142
+ } catch (error) {
143
+ debugIOS(`Backspace failed: ${error}`);
207
144
  }
208
- async getActiveElement() {
209
- this.ensureSession();
210
- debugIOS('Getting active element');
211
- try {
212
- var _response_value, _response_value1;
213
- const response = await this.makeRequest('GET', `/session/${this.sessionId}/element/active`);
214
- const elementId = (null == (_response_value = response.value) ? void 0 : _response_value.ELEMENT) || (null == (_response_value1 = response.value) ? void 0 : _response_value1['element-6066-11e4-a52e-4f735466cecf']) || response.ELEMENT || response['element-6066-11e4-a52e-4f735466cecf'];
215
- if (elementId) {
216
- debugIOS(`Got active element ID: ${elementId}`);
217
- return elementId;
218
- }
219
- debugIOS('No active element found');
220
- return null;
221
- } catch (error) {
222
- debugIOS(`Failed to get active element: ${error}`);
223
- return null;
224
- }
145
+ if ('Space' === key) try {
146
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
147
+ value: [
148
+ ' '
149
+ ]
150
+ });
151
+ debugIOS('Sent space character');
152
+ return;
153
+ } catch (error) {
154
+ debugIOS(`Space key failed: ${error}`);
155
+ }
156
+ const normalizedKey = this.normalizeKeyName(key);
157
+ const iosKeyMap = {
158
+ Tab: '\t',
159
+ ArrowUp: '\uE013',
160
+ ArrowDown: '\uE015',
161
+ ArrowLeft: '\uE012',
162
+ ArrowRight: '\uE014',
163
+ Home: '\uE011',
164
+ End: '\uE010'
165
+ };
166
+ if (iosKeyMap[normalizedKey]) try {
167
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
168
+ value: [
169
+ iosKeyMap[normalizedKey]
170
+ ]
171
+ });
172
+ debugIOS(`Sent WebDriver key code for: ${key}`);
173
+ return;
174
+ } catch (error) {
175
+ debugIOS(`WebDriver key failed for "${key}": ${error}`);
225
176
  }
226
- async clearElement(elementId) {
227
- this.ensureSession();
228
- debugIOS(`Clearing element: ${elementId}`);
229
- try {
230
- await this.makeRequest('POST', `/session/${this.sessionId}/element/${elementId}/clear`);
231
- debugIOS('Element cleared successfully');
232
- } catch (error) {
233
- debugIOS(`Failed to clear element: ${error}`);
234
- throw new Error(`Failed to clear element: ${error}`);
235
- }
177
+ if (1 === key.length) try {
178
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
179
+ value: [
180
+ key
181
+ ]
182
+ });
183
+ debugIOS(`Sent single character: "${key}"`);
184
+ return;
185
+ } catch (error) {
186
+ debugIOS(`Failed to send character "${key}": ${error}`);
236
187
  }
237
- async clearActiveElement() {
238
- try {
239
- const elementId = await this.getActiveElement();
240
- if (!elementId) {
241
- debugIOS('No active element to clear');
242
- return false;
243
- }
244
- await this.clearElement(elementId);
245
- return true;
246
- } catch (error) {
247
- debugIOS(`Failed to clear active element: ${error}`);
248
- return false;
188
+ debugIOS(`Warning: Key "${key}" is not supported on iOS platform`);
189
+ throw new Error(`Key "${key}" is not supported on iOS platform`);
190
+ }
191
+ async getActiveElement() {
192
+ this.ensureSession();
193
+ debugIOS('Getting active element');
194
+ try {
195
+ const response = await this.makeRequest('GET', `/session/${this.sessionId}/element/active`);
196
+ const elementId = response.value?.ELEMENT || response.value?.['element-6066-11e4-a52e-4f735466cecf'] || response.ELEMENT || response['element-6066-11e4-a52e-4f735466cecf'];
197
+ if (elementId) {
198
+ debugIOS(`Got active element ID: ${elementId}`);
199
+ return elementId;
249
200
  }
201
+ debugIOS('No active element found');
202
+ return null;
203
+ } catch (error) {
204
+ debugIOS(`Failed to get active element: ${error}`);
205
+ return null;
250
206
  }
251
- normalizeKeyName(key) {
252
- return key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
207
+ }
208
+ async clearElement(elementId) {
209
+ this.ensureSession();
210
+ debugIOS(`Clearing element: ${elementId}`);
211
+ try {
212
+ await this.makeRequest('POST', `/session/${this.sessionId}/element/${elementId}/clear`);
213
+ debugIOS('Element cleared successfully');
214
+ } catch (error) {
215
+ debugIOS(`Failed to clear element: ${error}`);
216
+ throw new Error(`Failed to clear element: ${error}`);
253
217
  }
254
- async dismissKeyboard(keyNames) {
255
- this.ensureSession();
256
- try {
257
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keyboard/dismiss`, {
258
- keyNames: keyNames || [
259
- 'done'
260
- ]
261
- });
262
- debugIOS('Dismissed keyboard using WDA API');
263
- return true;
264
- } catch (error) {
265
- debugIOS(`Failed to dismiss keyboard: ${error}`);
218
+ }
219
+ async clearActiveElement() {
220
+ try {
221
+ const elementId = await this.getActiveElement();
222
+ if (!elementId) {
223
+ debugIOS('No active element to clear');
266
224
  return false;
267
225
  }
226
+ await this.clearElement(elementId);
227
+ return true;
228
+ } catch (error) {
229
+ debugIOS(`Failed to clear active element: ${error}`);
230
+ return false;
268
231
  }
269
- async typeText(text) {
270
- this.ensureSession();
271
- try {
272
- const cleanText = text.trim();
273
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
274
- value: cleanText.split('')
275
- });
276
- debugIOS(`Typed text: "${text}"`);
277
- } catch (error) {
278
- debugIOS(`Failed to type text "${text}": ${error}`);
279
- throw new Error(`Failed to type text: ${error}`);
280
- }
281
- }
282
- async tap(x, y) {
283
- this.ensureSession();
284
- try {
285
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap`, {
286
- x,
287
- y
288
- });
289
- debugIOS(`Tapped at coordinates (${x}, ${y})`);
290
- } catch (error) {
291
- debugIOS(`New tap endpoint failed, trying legacy endpoint: ${error}`);
292
- try {
293
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap/0`, {
294
- x,
295
- y
296
- });
297
- debugIOS(`Tapped at coordinates (${x}, ${y}) using legacy endpoint`);
298
- } catch (fallbackError) {
299
- debugIOS(`Failed to tap at (${x}, ${y}): ${fallbackError}`);
300
- throw new Error(`Failed to tap at coordinates: ${fallbackError}`);
301
- }
302
- }
303
- }
304
- async swipe(fromX, fromY, toX, toY, duration = 500) {
305
- this.ensureSession();
306
- const actions = {
307
- actions: [
308
- {
309
- type: 'pointer',
310
- id: 'finger1',
311
- parameters: {
312
- pointerType: 'touch'
313
- },
314
- actions: [
315
- {
316
- type: 'pointerMove',
317
- duration: 0,
318
- x: fromX,
319
- y: fromY
320
- },
321
- {
322
- type: 'pointerDown',
323
- button: 0
324
- },
325
- {
326
- type: 'pause',
327
- duration: 100
328
- },
329
- {
330
- type: 'pointerMove',
331
- duration,
332
- x: toX,
333
- y: toY
334
- },
335
- {
336
- type: 'pointerUp',
337
- button: 0
338
- }
339
- ]
340
- }
232
+ }
233
+ normalizeKeyName(key) {
234
+ return key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
235
+ }
236
+ async dismissKeyboard(keyNames) {
237
+ this.ensureSession();
238
+ try {
239
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keyboard/dismiss`, {
240
+ keyNames: keyNames || [
241
+ 'done'
341
242
  ]
342
- };
343
- await this.makeRequest('POST', `/session/${this.sessionId}/actions`, actions);
344
- debugIOS(`Swiped using W3C Actions from (${fromX}, ${fromY}) to (${toX}, ${toY}) in ${duration}ms`);
345
- }
346
- async longPress(x, y, duration = 1000) {
347
- this.ensureSession();
348
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/touchAndHold`, {
349
- x,
350
- y,
351
- duration: duration / 1000
352
243
  });
353
- debugIOS(`Long pressed at coordinates (${x}, ${y}) for ${duration}ms`);
244
+ debugIOS('Dismissed keyboard using WDA API');
245
+ return true;
246
+ } catch (error) {
247
+ debugIOS(`Failed to dismiss keyboard: ${error}`);
248
+ return false;
354
249
  }
355
- async doubleTap(x, y) {
356
- this.ensureSession();
357
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/doubleTap`, {
358
- x,
359
- y
250
+ }
251
+ async typeText(text) {
252
+ this.ensureSession();
253
+ try {
254
+ const cleanText = text.trim();
255
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/keys`, {
256
+ value: cleanText.split('')
360
257
  });
361
- debugIOS(`Double tapped at coordinates (${x}, ${y})`);
258
+ debugIOS(`Typed text: "${text}"`);
259
+ } catch (error) {
260
+ debugIOS(`Failed to type text "${text}": ${error}`);
261
+ throw new Error(`Failed to type text: ${error}`);
362
262
  }
363
- async tripleTap(x, y) {
364
- this.ensureSession();
365
- await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
263
+ }
264
+ async tap(x, y) {
265
+ this.ensureSession();
266
+ try {
267
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap`, {
366
268
  x,
367
- y,
368
- numberOfTaps: 3,
369
- numberOfTouches: 1
269
+ y
370
270
  });
371
- debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
372
- }
373
- async getScreenScale() {
374
- this.ensureSession();
375
- try {
376
- var _screenResponse_value;
377
- const screenResponse = await this.makeRequest('GET', `/session/${this.sessionId}/wda/screen`);
378
- if (null == screenResponse ? void 0 : null == (_screenResponse_value = screenResponse.value) ? void 0 : _screenResponse_value.scale) {
379
- debugIOS(`Got screen scale from WDA screen endpoint: ${screenResponse.value.scale}`);
380
- return screenResponse.value.scale;
381
- }
382
- } catch (error) {
383
- debugIOS(`Failed to get screen scale from /wda/screen: ${error}`);
384
- }
271
+ debugIOS(`Tapped at coordinates (${x}, ${y})`);
272
+ } catch (error) {
273
+ debugIOS(`New tap endpoint failed, trying legacy endpoint: ${error}`);
385
274
  try {
386
- debugIOS('Calculating screen scale from screenshot and window size');
387
- const [screenshotBase64, windowSize] = await Promise.all([
388
- this.takeScreenshot(),
389
- this.getWindowSize()
390
- ]);
391
- const { jimpFromBase64 } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@midscene/shared/img"));
392
- const screenshotImg = await jimpFromBase64(screenshotBase64);
393
- const screenshotWidth = screenshotImg.bitmap.width;
394
- const screenshotHeight = screenshotImg.bitmap.height;
395
- const scale = Math.max(screenshotWidth, screenshotHeight) / Math.max(windowSize.width, windowSize.height);
396
- const roundedScale = Math.round(scale);
397
- debugIOS(`Calculated screen scale: ${roundedScale} (screenshot: ${screenshotWidth}x${screenshotHeight}, window: ${windowSize.width}x${windowSize.height})`);
398
- return roundedScale;
399
- } catch (error) {
400
- debugIOS(`Failed to calculate screen scale: ${error}`);
275
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tap/0`, {
276
+ x,
277
+ y
278
+ });
279
+ debugIOS(`Tapped at coordinates (${x}, ${y}) using legacy endpoint`);
280
+ } catch (fallbackError) {
281
+ debugIOS(`Failed to tap at (${x}, ${y}): ${fallbackError}`);
282
+ throw new Error(`Failed to tap at coordinates: ${fallbackError}`);
401
283
  }
402
- debugIOS('No screen scale found');
403
- return null;
404
- }
405
- async createSession(capabilities) {
406
- const defaultCapabilities = {
407
- platformName: 'iOS',
408
- automationName: 'XCUITest',
409
- shouldUseSingletonTestManager: false,
410
- shouldUseTestManagerForVisibilityDetection: false,
411
- ...capabilities
412
- };
413
- const session = await super.createSession(defaultCapabilities);
414
- await this.setupIOSSession();
415
- return session;
416
284
  }
417
- async setupIOSSession() {
418
- if (!this.sessionId) return;
419
- try {
420
- await this.makeRequest('POST', `/session/${this.sessionId}/appium/settings`, {
421
- snapshotMaxDepth: 50,
422
- elementResponseAttributes: 'type,label,name,value,rect,enabled,visible'
423
- });
424
- debugIOS('iOS session configuration applied');
425
- } catch (error) {
426
- debugIOS(`Failed to apply iOS session configuration: ${error}`);
285
+ }
286
+ async swipe(fromX, fromY, toX, toY, duration = 500) {
287
+ this.ensureSession();
288
+ const actions = {
289
+ actions: [
290
+ {
291
+ type: 'pointer',
292
+ id: 'finger1',
293
+ parameters: {
294
+ pointerType: 'touch'
295
+ },
296
+ actions: [
297
+ {
298
+ type: 'pointerMove',
299
+ duration: 0,
300
+ x: fromX,
301
+ y: fromY
302
+ },
303
+ {
304
+ type: 'pointerDown',
305
+ button: 0
306
+ },
307
+ {
308
+ type: 'pause',
309
+ duration: 100
310
+ },
311
+ {
312
+ type: 'pointerMove',
313
+ duration,
314
+ x: toX,
315
+ y: toY
316
+ },
317
+ {
318
+ type: 'pointerUp',
319
+ button: 0
320
+ }
321
+ ]
322
+ }
323
+ ]
324
+ };
325
+ await this.makeRequest('POST', `/session/${this.sessionId}/actions`, actions);
326
+ debugIOS(`Swiped using W3C Actions from (${fromX}, ${fromY}) to (${toX}, ${toY}) in ${duration}ms`);
327
+ }
328
+ async longPress(x, y, duration = 1000) {
329
+ this.ensureSession();
330
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/touchAndHold`, {
331
+ x,
332
+ y,
333
+ duration: duration / 1000
334
+ });
335
+ debugIOS(`Long pressed at coordinates (${x}, ${y}) for ${duration}ms`);
336
+ }
337
+ async doubleTap(x, y) {
338
+ this.ensureSession();
339
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/doubleTap`, {
340
+ x,
341
+ y
342
+ });
343
+ debugIOS(`Double tapped at coordinates (${x}, ${y})`);
344
+ }
345
+ async tripleTap(x, y) {
346
+ this.ensureSession();
347
+ await this.makeRequest('POST', `/session/${this.sessionId}/wda/tapWithNumberOfTaps`, {
348
+ x,
349
+ y,
350
+ numberOfTaps: 3,
351
+ numberOfTouches: 1
352
+ });
353
+ debugIOS(`Triple tapped at coordinates (${x}, ${y})`);
354
+ }
355
+ async getScreenScale() {
356
+ this.ensureSession();
357
+ try {
358
+ const screenResponse = await this.makeRequest('GET', `/session/${this.sessionId}/wda/screen`);
359
+ if (screenResponse?.value?.scale) {
360
+ debugIOS(`Got screen scale from WDA screen endpoint: ${screenResponse.value.scale}`);
361
+ return screenResponse.value.scale;
427
362
  }
363
+ } catch (error) {
364
+ debugIOS(`Failed to get screen scale from /wda/screen: ${error}`);
428
365
  }
429
- async executeRequest(method, endpoint, data) {
430
- this.ensureSession();
431
- return this.makeRequest(method, endpoint, data);
366
+ try {
367
+ debugIOS('Calculating screen scale from screenshot and window size');
368
+ const [screenshotBase64, windowSize] = await Promise.all([
369
+ this.takeScreenshot(),
370
+ this.getWindowSize()
371
+ ]);
372
+ const { jimpFromBase64 } = await import("@midscene/shared/img");
373
+ const screenshotImg = await jimpFromBase64(screenshotBase64);
374
+ const screenshotWidth = screenshotImg.bitmap.width;
375
+ const screenshotHeight = screenshotImg.bitmap.height;
376
+ const scale = Math.max(screenshotWidth, screenshotHeight) / Math.max(windowSize.width, windowSize.height);
377
+ const roundedScale = Math.round(scale);
378
+ debugIOS(`Calculated screen scale: ${roundedScale} (screenshot: ${screenshotWidth}x${screenshotHeight}, window: ${windowSize.width}x${windowSize.height})`);
379
+ return roundedScale;
380
+ } catch (error) {
381
+ debugIOS(`Failed to calculate screen scale: ${error}`);
432
382
  }
383
+ debugIOS('No screen scale found');
384
+ return null;
433
385
  }
434
- function _define_property(obj, key, value) {
435
- if (key in obj) Object.defineProperty(obj, key, {
436
- value: value,
437
- enumerable: true,
438
- configurable: true,
439
- writable: true
440
- });
441
- else obj[key] = value;
442
- return obj;
443
- }
444
- const debugDevice = (0, logger_namespaceObject.getDebug)('ios:device');
445
- const WDA_HTTP_METHODS = [
446
- 'GET',
447
- 'POST',
448
- 'DELETE',
449
- 'PUT'
450
- ];
451
- class IOSDevice {
452
- actionSpace() {
453
- const defaultActions = [
454
- (0, device_namespaceObject.defineActionTap)(async (param)=>{
455
- const element = param.locate;
456
- external_node_assert_default()(element, 'Element not found, cannot tap');
457
- await this.mouseClick(element.center[0], element.center[1]);
458
- }),
459
- (0, device_namespaceObject.defineActionDoubleClick)(async (param)=>{
460
- const element = param.locate;
461
- external_node_assert_default()(element, 'Element not found, cannot double click');
462
- await this.doubleTap(element.center[0], element.center[1]);
463
- }),
464
- (0, device_namespaceObject.defineAction)({
465
- name: 'Input',
466
- description: 'Input text into the input field',
467
- interfaceAlias: 'aiInput',
468
- paramSchema: core_namespaceObject.z.object({
469
- value: core_namespaceObject.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.'),
470
- autoDismissKeyboard: core_namespaceObject.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.'),
471
- mode: core_namespaceObject.z["enum"]([
472
- 'replace',
473
- 'clear',
474
- 'append'
475
- ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
476
- locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
477
- }),
478
- call: async (param)=>{
479
- var _this_options;
480
- const element = param.locate;
481
- if (element) {
482
- if ('append' !== param.mode) await this.clearInput(element);
483
- }
484
- if ('clear' === param.mode) return;
485
- if (!param || !param.value) return;
486
- const autoDismissKeyboard = param.autoDismissKeyboard ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard);
487
- await this.typeText(param.value, {
488
- autoDismissKeyboard
489
- });
490
- }
386
+ async createSession(capabilities) {
387
+ const defaultCapabilities = {
388
+ platformName: 'iOS',
389
+ automationName: 'XCUITest',
390
+ shouldUseSingletonTestManager: false,
391
+ shouldUseTestManagerForVisibilityDetection: false,
392
+ ...capabilities
393
+ };
394
+ const session = await super.createSession(defaultCapabilities);
395
+ await this.setupIOSSession();
396
+ return session;
397
+ }
398
+ async setupIOSSession() {
399
+ if (!this.sessionId) return;
400
+ try {
401
+ await this.makeRequest('POST', `/session/${this.sessionId}/appium/settings`, {
402
+ snapshotMaxDepth: 50,
403
+ elementResponseAttributes: 'type,label,name,value,rect,enabled,visible'
404
+ });
405
+ debugIOS('iOS session configuration applied');
406
+ } catch (error) {
407
+ debugIOS(`Failed to apply iOS session configuration: ${error}`);
408
+ }
409
+ }
410
+ async executeRequest(method, endpoint, data) {
411
+ this.ensureSession();
412
+ return this.makeRequest(method, endpoint, data);
413
+ }
414
+ }
415
+ function _define_property(obj, key, value) {
416
+ if (key in obj) Object.defineProperty(obj, key, {
417
+ value: value,
418
+ enumerable: true,
419
+ configurable: true,
420
+ writable: true
421
+ });
422
+ else obj[key] = value;
423
+ return obj;
424
+ }
425
+ const debugDevice = (0, logger_namespaceObject.getDebug)('ios:device');
426
+ const WDA_HTTP_METHODS = [
427
+ 'GET',
428
+ 'POST',
429
+ 'DELETE',
430
+ 'PUT'
431
+ ];
432
+ class IOSDevice {
433
+ actionSpace() {
434
+ const defaultActions = [
435
+ (0, device_namespaceObject.defineActionTap)(async (param)=>{
436
+ const element = param.locate;
437
+ external_node_assert_default()(element, 'Element not found, cannot tap');
438
+ await this.mouseClick(element.center[0], element.center[1]);
439
+ }),
440
+ (0, device_namespaceObject.defineActionDoubleClick)(async (param)=>{
441
+ const element = param.locate;
442
+ external_node_assert_default()(element, 'Element not found, cannot double click');
443
+ await this.doubleTap(element.center[0], element.center[1]);
444
+ }),
445
+ (0, device_namespaceObject.defineAction)({
446
+ name: 'Input',
447
+ description: 'Input text into the input field',
448
+ interfaceAlias: 'aiInput',
449
+ paramSchema: core_namespaceObject.z.object({
450
+ value: core_namespaceObject.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.'),
451
+ autoDismissKeyboard: core_namespaceObject.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.'),
452
+ mode: core_namespaceObject.z["enum"]([
453
+ 'replace',
454
+ 'clear',
455
+ 'append'
456
+ ]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "append" - append the value to existing content; "clear" - clear the field without inputting new text.'),
457
+ locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
491
458
  }),
492
- (0, device_namespaceObject.defineActionScroll)(async (param)=>{
459
+ call: async (param)=>{
493
460
  const element = param.locate;
494
- const startingPoint = element ? {
495
- left: element.center[0],
496
- top: element.center[1]
497
- } : void 0;
498
- const scrollToEventName = null == param ? void 0 : param.scrollType;
499
- if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
500
- else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
501
- else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
502
- else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
503
- else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
504
- else {
505
- if ((null == param ? void 0 : param.direction) !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
506
- else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
507
- else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
508
- else throw new Error(`Unknown scroll direction: ${param.direction}`);
509
- else await this.scrollDown((null == param ? void 0 : param.distance) || void 0, startingPoint);
510
- await (0, utils_namespaceObject.sleep)(500);
511
- }
512
- }),
513
- (0, device_namespaceObject.defineActionDragAndDrop)(async (param)=>{
514
- const from = param.from;
515
- const to = param.to;
516
- external_node_assert_default()(from, 'missing "from" param for drag and drop');
517
- external_node_assert_default()(to, 'missing "to" param for drag and drop');
518
- await this.swipe(from.center[0], from.center[1], to.center[0], to.center[1]);
519
- }),
520
- (0, device_namespaceObject.defineActionKeyboardPress)(async (param)=>{
521
- await this.pressKey(param.keyName);
522
- }),
523
- (0, device_namespaceObject.defineAction)({
524
- name: 'IOSLongPress',
525
- description: 'Trigger a long press on the screen at specified coordinates on iOS devices',
526
- paramSchema: core_namespaceObject.z.object({
527
- duration: core_namespaceObject.z.number().optional().describe('The duration of the long press in milliseconds'),
528
- locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The element to be long pressed')
529
- }),
530
- call: async (param)=>{
531
- const element = param.locate;
532
- external_node_assert_default()(element, 'IOSLongPress requires an element to be located');
533
- const [x, y] = element.center;
534
- await this.longPress(x, y, null == param ? void 0 : param.duration);
461
+ if (element) {
462
+ if ('append' !== param.mode) await this.clearInput(element);
535
463
  }
464
+ if ('clear' === param.mode) return;
465
+ if (!param || !param.value) return;
466
+ const autoDismissKeyboard = param.autoDismissKeyboard ?? this.options?.autoDismissKeyboard;
467
+ await this.typeText(param.value, {
468
+ autoDismissKeyboard
469
+ });
470
+ }
471
+ }),
472
+ (0, device_namespaceObject.defineActionScroll)(async (param)=>{
473
+ const element = param.locate;
474
+ const startingPoint = element ? {
475
+ left: element.center[0],
476
+ top: element.center[1]
477
+ } : void 0;
478
+ const scrollToEventName = param?.scrollType;
479
+ if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
480
+ else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
481
+ else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
482
+ else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
483
+ else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
484
+ else {
485
+ if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance || void 0, startingPoint);
486
+ else if ('left' === param.direction) await this.scrollLeft(param.distance || void 0, startingPoint);
487
+ else if ('right' === param.direction) await this.scrollRight(param.distance || void 0, startingPoint);
488
+ else throw new Error(`Unknown scroll direction: ${param.direction}`);
489
+ else await this.scrollDown(param?.distance || void 0, startingPoint);
490
+ await (0, utils_namespaceObject.sleep)(500);
491
+ }
492
+ }),
493
+ (0, device_namespaceObject.defineActionDragAndDrop)(async (param)=>{
494
+ const from = param.from;
495
+ const to = param.to;
496
+ external_node_assert_default()(from, 'missing "from" param for drag and drop');
497
+ external_node_assert_default()(to, 'missing "to" param for drag and drop');
498
+ await this.swipe(from.center[0], from.center[1], to.center[0], to.center[1]);
499
+ }),
500
+ (0, device_namespaceObject.defineActionKeyboardPress)(async (param)=>{
501
+ await this.pressKey(param.keyName);
502
+ }),
503
+ (0, device_namespaceObject.defineAction)({
504
+ name: 'IOSLongPress',
505
+ description: 'Trigger a long press on the screen at specified coordinates on iOS devices',
506
+ paramSchema: core_namespaceObject.z.object({
507
+ duration: core_namespaceObject.z.number().optional().describe('The duration of the long press in milliseconds'),
508
+ locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The element to be long pressed')
536
509
  }),
537
- (0, device_namespaceObject.defineActionClearInput)(async (param)=>{
510
+ call: async (param)=>{
538
511
  const element = param.locate;
539
- external_node_assert_default()(element, 'Element not found, cannot clear input');
540
- await this.clearInput(element);
541
- })
542
- ];
543
- const platformSpecificActions = Object.values(createPlatformActions(this));
544
- const customActions = this.customActions || [];
545
- return [
546
- ...defaultActions,
547
- ...platformSpecificActions,
548
- ...customActions
549
- ];
550
- }
551
- describe() {
552
- return this.description || `Device ID: ${this.deviceId}`;
553
- }
554
- async getConnectedDeviceInfo() {
555
- return await this.wdaBackend.getDeviceInfo();
556
- }
557
- async connect() {
558
- external_node_assert_default()(!this.destroyed, `IOSDevice ${this.deviceId} has been destroyed and cannot execute commands`);
559
- debugDevice(`Connecting to iOS device: ${this.deviceId}`);
560
- try {
561
- await this.wdaManager.start();
562
- await this.wdaBackend.createSession();
563
- const deviceInfo = await this.wdaBackend.getDeviceInfo();
564
- if (null == deviceInfo ? void 0 : deviceInfo.udid) {
565
- this.deviceId = deviceInfo.udid;
566
- debugDevice(`Updated device ID to real UDID: ${this.deviceId}`);
512
+ external_node_assert_default()(element, 'IOSLongPress requires an element to be located');
513
+ const [x, y] = element.center;
514
+ await this.longPress(x, y, param?.duration);
567
515
  }
568
- const size = await this.getScreenSize();
569
- this.description = `
516
+ }),
517
+ (0, device_namespaceObject.defineActionClearInput)(async (param)=>{
518
+ const element = param.locate;
519
+ external_node_assert_default()(element, 'Element not found, cannot clear input');
520
+ await this.clearInput(element);
521
+ })
522
+ ];
523
+ const platformSpecificActions = Object.values(createPlatformActions(this));
524
+ const customActions = this.customActions || [];
525
+ return [
526
+ ...defaultActions,
527
+ ...platformSpecificActions,
528
+ ...customActions
529
+ ];
530
+ }
531
+ describe() {
532
+ return this.description || `Device ID: ${this.deviceId}`;
533
+ }
534
+ async getConnectedDeviceInfo() {
535
+ return await this.wdaBackend.getDeviceInfo();
536
+ }
537
+ async connect() {
538
+ external_node_assert_default()(!this.destroyed, `IOSDevice ${this.deviceId} has been destroyed and cannot execute commands`);
539
+ debugDevice(`Connecting to iOS device: ${this.deviceId}`);
540
+ try {
541
+ await this.wdaManager.start();
542
+ await this.wdaBackend.createSession();
543
+ const deviceInfo = await this.wdaBackend.getDeviceInfo();
544
+ if (deviceInfo?.udid) {
545
+ this.deviceId = deviceInfo.udid;
546
+ debugDevice(`Updated device ID to real UDID: ${this.deviceId}`);
547
+ }
548
+ const size = await this.getScreenSize();
549
+ this.description = `
570
550
  UDID: ${this.deviceId}${deviceInfo ? `
571
551
  Name: ${deviceInfo.name}
572
552
  Model: ${deviceInfo.model}` : ''}
573
553
  Type: WebDriverAgent
574
554
  ScreenSize: ${size.width}x${size.height} (DPR: ${size.scale})
575
555
  `;
576
- debugDevice('iOS device connected successfully', this.description);
577
- } catch (e) {
578
- debugDevice(`Failed to connect to iOS device: ${e}`);
579
- throw new Error(`Unable to connect to iOS device ${this.deviceId}: ${e}`);
580
- }
581
- }
582
- async launch(uri) {
583
- this.uri = uri;
584
- try {
585
- debugDevice(`Launching app: ${uri}`);
586
- if (uri.startsWith('http://') || uri.startsWith('https://') || uri.includes('://')) await this.openUrl(uri);
587
- else await this.wdaBackend.launchApp(uri);
588
- debugDevice(`Successfully launched: ${uri}`);
589
- } catch (error) {
590
- debugDevice(`Error launching ${uri}: ${error}`);
591
- throw new Error(`Failed to launch ${uri}: ${error.message}`);
592
- }
593
- return this;
594
- }
595
- async getElementsInfo() {
596
- return [];
597
- }
598
- async getElementsNodeTree() {
599
- return {
600
- node: null,
601
- children: []
602
- };
603
- }
604
- async initializeDevicePixelRatio() {
605
- if (this.devicePixelRatioInitialized) return;
606
- const apiScale = await this.wdaBackend.getScreenScale();
607
- external_node_assert_default()(apiScale && apiScale > 0, 'Failed to get device pixel ratio from WebDriverAgent API');
608
- debugDevice(`Got screen scale from WebDriverAgent API: ${apiScale}`);
609
- this.devicePixelRatio = apiScale;
610
- this.devicePixelRatioInitialized = true;
611
- }
612
- async getScreenSize() {
613
- await this.initializeDevicePixelRatio();
614
- const windowSize = await this.wdaBackend.getWindowSize();
615
- return {
616
- width: windowSize.width,
617
- height: windowSize.height,
618
- scale: this.devicePixelRatio
619
- };
620
- }
621
- async size() {
622
- const screenSize = await this.getScreenSize();
623
- return {
624
- width: screenSize.width,
625
- height: screenSize.height,
626
- dpr: screenSize.scale
627
- };
628
- }
629
- async screenshotBase64() {
630
- debugDevice('Taking screenshot via WDA');
631
- try {
632
- const base64Data = await this.wdaBackend.takeScreenshot();
633
- const result = (0, img_namespaceObject.createImgBase64ByFormat)('png', base64Data);
634
- debugDevice('Screenshot taken successfully');
635
- return result;
636
- } catch (error) {
637
- debugDevice(`Screenshot failed: ${error}`);
638
- throw new Error(`Failed to take screenshot: ${error}`);
639
- }
640
- }
641
- async clearInput(element) {
642
- if (!element) return;
643
- await this.tap(element.center[0], element.center[1]);
644
- await (0, utils_namespaceObject.sleep)(100);
645
- debugDevice('Attempting to clear input with WebDriver Clear API');
646
- const cleared = await this.wdaBackend.clearActiveElement();
647
- cleared ? debugDevice('Successfully cleared input with WebDriver Clear API') : debugDevice('WebDriver Clear API returned false (no active element or clear failed)');
556
+ debugDevice('iOS device connected successfully', this.description);
557
+ } catch (e) {
558
+ debugDevice(`Failed to connect to iOS device: ${e}`);
559
+ throw new Error(`Unable to connect to iOS device ${this.deviceId}: ${e}`);
648
560
  }
649
- async url() {
650
- return '';
651
- }
652
- async tap(x, y) {
653
- await this.wdaBackend.tap(Math.round(x), Math.round(y));
654
- }
655
- async mouseClick(x, y) {
656
- debugDevice(`mouseClick at coordinates (${x}, ${y})`);
657
- await this.tap(x, y);
658
- }
659
- async doubleTap(x, y) {
660
- await this.wdaBackend.doubleTap(Math.round(x), Math.round(y));
661
- }
662
- async tripleTap(x, y) {
663
- await this.wdaBackend.tripleTap(Math.round(x), Math.round(y));
664
- }
665
- async longPress(x, y, duration = 1000) {
666
- await this.wdaBackend.longPress(Math.round(x), Math.round(y), duration);
667
- }
668
- async swipe(fromX, fromY, toX, toY, duration = 500) {
669
- await this.wdaBackend.swipe(Math.round(fromX), Math.round(fromY), Math.round(toX), Math.round(toY), duration);
670
- }
671
- async typeText(text, options) {
672
- var _this_options;
673
- if (!text) return;
674
- const shouldAutoDismissKeyboard = (null == options ? void 0 : options.autoDismissKeyboard) ?? (null == (_this_options = this.options) ? void 0 : _this_options.autoDismissKeyboard) ?? true;
675
- debugDevice(`Typing text: "${text}"`);
676
- try {
677
- await (0, utils_namespaceObject.sleep)(200);
678
- await this.wdaBackend.typeText(text);
679
- await (0, utils_namespaceObject.sleep)(300);
680
- } catch (error) {
681
- debugDevice(`Failed to type text with WDA: ${error}`);
682
- throw error;
683
- }
684
- if (shouldAutoDismissKeyboard) await this.hideKeyboard();
685
- }
686
- async pressKey(key) {
687
- await this.wdaBackend.pressKey(key);
688
- }
689
- async scrollUp(distance, startPoint) {
690
- const { width, height } = await this.size();
691
- const start = startPoint ? {
692
- x: Math.round(startPoint.left),
693
- y: Math.round(startPoint.top)
694
- } : {
695
- x: Math.round(width / 2),
696
- y: Math.round(height / 2)
697
- };
698
- const scrollDistance = Math.round(distance || height / 3);
699
- await this.swipe(start.x, start.y, start.x, start.y + scrollDistance);
700
- }
701
- async scrollDown(distance, startPoint) {
702
- const { width, height } = await this.size();
703
- const start = startPoint ? {
704
- x: Math.round(startPoint.left),
705
- y: Math.round(startPoint.top)
706
- } : {
707
- x: Math.round(width / 2),
708
- y: Math.round(height / 2)
709
- };
710
- const scrollDistance = Math.round(distance || height / 3);
711
- await this.swipe(start.x, start.y, start.x, start.y - scrollDistance);
712
- }
713
- async scrollLeft(distance, startPoint) {
714
- const { width, height } = await this.size();
715
- const start = startPoint ? {
716
- x: Math.round(startPoint.left),
717
- y: Math.round(startPoint.top)
718
- } : {
719
- x: Math.round(width / 2),
720
- y: Math.round(height / 2)
721
- };
722
- const scrollDistance = Math.round(distance || 0.7 * width);
723
- await this.swipe(start.x, start.y, start.x + scrollDistance, start.y);
561
+ }
562
+ async launch(uri) {
563
+ this.uri = uri;
564
+ try {
565
+ debugDevice(`Launching app: ${uri}`);
566
+ if (uri.startsWith('http://') || uri.startsWith('https://') || uri.includes('://')) await this.openUrl(uri);
567
+ else await this.wdaBackend.launchApp(uri);
568
+ debugDevice(`Successfully launched: ${uri}`);
569
+ } catch (error) {
570
+ debugDevice(`Error launching ${uri}: ${error}`);
571
+ throw new Error(`Failed to launch ${uri}: ${error.message}`);
724
572
  }
725
- async scrollRight(distance, startPoint) {
726
- const { width, height } = await this.size();
727
- const start = startPoint ? {
728
- x: Math.round(startPoint.left),
729
- y: Math.round(startPoint.top)
730
- } : {
731
- x: Math.round(width / 2),
732
- y: Math.round(height / 2)
733
- };
734
- const scrollDistance = Math.round(distance || 0.7 * width);
735
- await this.swipe(start.x, start.y, start.x - scrollDistance, start.y);
573
+ return this;
574
+ }
575
+ async getElementsInfo() {
576
+ return [];
577
+ }
578
+ async getElementsNodeTree() {
579
+ return {
580
+ node: null,
581
+ children: []
582
+ };
583
+ }
584
+ async initializeDevicePixelRatio() {
585
+ if (this.devicePixelRatioInitialized) return;
586
+ const apiScale = await this.wdaBackend.getScreenScale();
587
+ external_node_assert_default()(apiScale && apiScale > 0, 'Failed to get device pixel ratio from WebDriverAgent API');
588
+ debugDevice(`Got screen scale from WebDriverAgent API: ${apiScale}`);
589
+ this.devicePixelRatio = apiScale;
590
+ this.devicePixelRatioInitialized = true;
591
+ }
592
+ async getScreenSize() {
593
+ await this.initializeDevicePixelRatio();
594
+ const windowSize = await this.wdaBackend.getWindowSize();
595
+ return {
596
+ width: windowSize.width,
597
+ height: windowSize.height,
598
+ scale: this.devicePixelRatio
599
+ };
600
+ }
601
+ async size() {
602
+ const screenSize = await this.getScreenSize();
603
+ return {
604
+ width: screenSize.width,
605
+ height: screenSize.height,
606
+ dpr: screenSize.scale
607
+ };
608
+ }
609
+ async screenshotBase64() {
610
+ debugDevice('Taking screenshot via WDA');
611
+ try {
612
+ const base64Data = await this.wdaBackend.takeScreenshot();
613
+ const result = (0, img_namespaceObject.createImgBase64ByFormat)('png', base64Data);
614
+ debugDevice('Screenshot taken successfully');
615
+ return result;
616
+ } catch (error) {
617
+ debugDevice(`Screenshot failed: ${error}`);
618
+ throw new Error(`Failed to take screenshot: ${error}`);
736
619
  }
737
- async scrollUntilTop(startPoint) {
738
- debugDevice('Using screenshot-based scroll detection for better reliability');
739
- await this.scrollUntilBoundary('up', startPoint, 1);
620
+ }
621
+ async clearInput(element) {
622
+ if (!element) return;
623
+ await this.tap(element.center[0], element.center[1]);
624
+ await (0, utils_namespaceObject.sleep)(100);
625
+ debugDevice('Attempting to clear input with WebDriver Clear API');
626
+ const cleared = await this.wdaBackend.clearActiveElement();
627
+ cleared ? debugDevice('Successfully cleared input with WebDriver Clear API') : debugDevice('WebDriver Clear API returned false (no active element or clear failed)');
628
+ }
629
+ async url() {
630
+ return '';
631
+ }
632
+ async tap(x, y) {
633
+ await this.wdaBackend.tap(Math.round(x), Math.round(y));
634
+ }
635
+ async mouseClick(x, y) {
636
+ debugDevice(`mouseClick at coordinates (${x}, ${y})`);
637
+ await this.tap(x, y);
638
+ }
639
+ async doubleTap(x, y) {
640
+ await this.wdaBackend.doubleTap(Math.round(x), Math.round(y));
641
+ }
642
+ async tripleTap(x, y) {
643
+ await this.wdaBackend.tripleTap(Math.round(x), Math.round(y));
644
+ }
645
+ async longPress(x, y, duration = 1000) {
646
+ await this.wdaBackend.longPress(Math.round(x), Math.round(y), duration);
647
+ }
648
+ async swipe(fromX, fromY, toX, toY, duration = 500) {
649
+ await this.wdaBackend.swipe(Math.round(fromX), Math.round(fromY), Math.round(toX), Math.round(toY), duration);
650
+ }
651
+ async typeText(text, options) {
652
+ if (!text) return;
653
+ const shouldAutoDismissKeyboard = options?.autoDismissKeyboard ?? this.options?.autoDismissKeyboard ?? true;
654
+ debugDevice(`Typing text: "${text}"`);
655
+ try {
656
+ await (0, utils_namespaceObject.sleep)(200);
657
+ await this.wdaBackend.typeText(text);
658
+ await (0, utils_namespaceObject.sleep)(300);
659
+ } catch (error) {
660
+ debugDevice(`Failed to type text with WDA: ${error}`);
661
+ throw error;
740
662
  }
741
- async scrollUntilBottom(startPoint) {
742
- debugDevice('Using screenshot-based scroll detection for better reliability');
743
- await this.scrollUntilBoundary('down', startPoint, 1);
663
+ if (shouldAutoDismissKeyboard) await this.hideKeyboard();
664
+ }
665
+ async pressKey(key) {
666
+ await this.wdaBackend.pressKey(key);
667
+ }
668
+ async scrollUp(distance, startPoint) {
669
+ const { width, height } = await this.size();
670
+ const start = startPoint ? {
671
+ x: Math.round(startPoint.left),
672
+ y: Math.round(startPoint.top)
673
+ } : {
674
+ x: Math.round(width / 2),
675
+ y: Math.round(height / 2)
676
+ };
677
+ const scrollDistance = Math.round(distance || height / 3);
678
+ await this.swipe(start.x, start.y, start.x, start.y + scrollDistance);
679
+ }
680
+ async scrollDown(distance, startPoint) {
681
+ const { width, height } = await this.size();
682
+ const start = startPoint ? {
683
+ x: Math.round(startPoint.left),
684
+ y: Math.round(startPoint.top)
685
+ } : {
686
+ x: Math.round(width / 2),
687
+ y: Math.round(height / 2)
688
+ };
689
+ const scrollDistance = Math.round(distance || height / 3);
690
+ await this.swipe(start.x, start.y, start.x, start.y - scrollDistance);
691
+ }
692
+ async scrollLeft(distance, startPoint) {
693
+ const { width, height } = await this.size();
694
+ const start = startPoint ? {
695
+ x: Math.round(startPoint.left),
696
+ y: Math.round(startPoint.top)
697
+ } : {
698
+ x: Math.round(width / 2),
699
+ y: Math.round(height / 2)
700
+ };
701
+ const scrollDistance = Math.round(distance || 0.7 * width);
702
+ await this.swipe(start.x, start.y, start.x + scrollDistance, start.y);
703
+ }
704
+ async scrollRight(distance, startPoint) {
705
+ const { width, height } = await this.size();
706
+ const start = startPoint ? {
707
+ x: Math.round(startPoint.left),
708
+ y: Math.round(startPoint.top)
709
+ } : {
710
+ x: Math.round(width / 2),
711
+ y: Math.round(height / 2)
712
+ };
713
+ const scrollDistance = Math.round(distance || 0.7 * width);
714
+ await this.swipe(start.x, start.y, start.x - scrollDistance, start.y);
715
+ }
716
+ async scrollUntilTop(startPoint) {
717
+ debugDevice('Using screenshot-based scroll detection for better reliability');
718
+ await this.scrollUntilBoundary('up', startPoint, 1);
719
+ }
720
+ async scrollUntilBottom(startPoint) {
721
+ debugDevice('Using screenshot-based scroll detection for better reliability');
722
+ await this.scrollUntilBoundary('down', startPoint, 1);
723
+ }
724
+ compareScreenshots(screenshot1, screenshot2, tolerancePercent = 2) {
725
+ if (screenshot1 === screenshot2) {
726
+ debugDevice('Screenshots are identical');
727
+ return true;
728
+ }
729
+ const len1 = screenshot1.length;
730
+ const len2 = screenshot2.length;
731
+ debugDevice(`Screenshots differ: length1=${len1}, length2=${len2}`);
732
+ if (Math.abs(len1 - len2) > 0.1 * Math.min(len1, len2)) {
733
+ debugDevice('Screenshots have significant length difference');
734
+ return false;
744
735
  }
745
- compareScreenshots(screenshot1, screenshot2, tolerancePercent = 2) {
746
- if (screenshot1 === screenshot2) {
747
- debugDevice('Screenshots are identical');
748
- return true;
749
- }
750
- const len1 = screenshot1.length;
751
- const len2 = screenshot2.length;
752
- debugDevice(`Screenshots differ: length1=${len1}, length2=${len2}`);
753
- if (Math.abs(len1 - len2) > 0.1 * Math.min(len1, len2)) {
754
- debugDevice('Screenshots have significant length difference');
755
- return false;
736
+ if (len1 > 0 && len2 > 0) {
737
+ const minLength = Math.min(len1, len2);
738
+ const sampleSize = Math.min(2000, minLength);
739
+ let diffCount = 0;
740
+ for(let i = 0; i < sampleSize; i++)if (screenshot1[i] !== screenshot2[i]) diffCount++;
741
+ const diffPercent = diffCount / sampleSize * 100;
742
+ debugDevice(`Character differences: ${diffCount}/${sampleSize} (${diffPercent.toFixed(2)}%)`);
743
+ const isSimilar = diffPercent <= tolerancePercent;
744
+ if (isSimilar) debugDevice(`Screenshots are similar enough (${diffPercent.toFixed(2)}% <= ${tolerancePercent}%)`);
745
+ return isSimilar;
746
+ }
747
+ return false;
748
+ }
749
+ async scrollUntilBoundary(direction, startPoint, maxUnchangedCount = 1) {
750
+ const maxAttempts = 20;
751
+ const { width, height } = await this.size();
752
+ let start;
753
+ if (startPoint) start = {
754
+ x: Math.round(startPoint.left),
755
+ y: Math.round(startPoint.top)
756
+ };
757
+ else switch(direction){
758
+ case 'up':
759
+ start = {
760
+ x: Math.round(width / 2),
761
+ y: Math.round(0.2 * height)
762
+ };
763
+ break;
764
+ case 'down':
765
+ start = {
766
+ x: Math.round(width / 2),
767
+ y: Math.round(0.8 * height)
768
+ };
769
+ break;
770
+ case 'left':
771
+ start = {
772
+ x: Math.round(0.8 * width),
773
+ y: Math.round(height / 2)
774
+ };
775
+ break;
776
+ case 'right':
777
+ start = {
778
+ x: Math.round(0.2 * width),
779
+ y: Math.round(height / 2)
780
+ };
781
+ break;
782
+ }
783
+ let lastScreenshot = null;
784
+ let unchangedCount = 0;
785
+ debugDevice(`Starting scroll to ${direction} with content detection`);
786
+ for(let i = 0; i < maxAttempts; i++)try {
787
+ debugDevice(`Scroll attempt ${i + 1}/${maxAttempts}`);
788
+ await (0, utils_namespaceObject.sleep)(500);
789
+ const currentScreenshot = await this.screenshotBase64();
790
+ if (lastScreenshot && this.compareScreenshots(lastScreenshot, currentScreenshot, 10)) {
791
+ unchangedCount++;
792
+ debugDevice(`Screen content unchanged (${unchangedCount}/${maxUnchangedCount})`);
793
+ if (unchangedCount >= maxUnchangedCount) {
794
+ debugDevice(`Reached ${direction}: screen content no longer changes`);
795
+ break;
796
+ }
797
+ } else {
798
+ if (lastScreenshot) debugDevice(`Content changed, resetting counter (was ${unchangedCount})`);
799
+ unchangedCount = 0;
756
800
  }
757
- if (len1 > 0 && len2 > 0) {
758
- const minLength = Math.min(len1, len2);
759
- const sampleSize = Math.min(2000, minLength);
760
- let diffCount = 0;
761
- for(let i = 0; i < sampleSize; i++)if (screenshot1[i] !== screenshot2[i]) diffCount++;
762
- const diffPercent = diffCount / sampleSize * 100;
763
- debugDevice(`Character differences: ${diffCount}/${sampleSize} (${diffPercent.toFixed(2)}%)`);
764
- const isSimilar = diffPercent <= tolerancePercent;
765
- if (isSimilar) debugDevice(`Screenshots are similar enough (${diffPercent.toFixed(2)}% <= ${tolerancePercent}%)`);
766
- return isSimilar;
801
+ if (i >= 15 && 0 === unchangedCount) {
802
+ debugDevice(`Too many attempts with dynamic content, stopping scroll to ${direction}`);
803
+ break;
767
804
  }
768
- return false;
769
- }
770
- async scrollUntilBoundary(direction, startPoint, maxUnchangedCount = 1) {
771
- const maxAttempts = 20;
772
- const { width, height } = await this.size();
773
- let start;
774
- if (startPoint) start = {
775
- x: Math.round(startPoint.left),
776
- y: Math.round(startPoint.top)
777
- };
778
- else switch(direction){
805
+ lastScreenshot = currentScreenshot;
806
+ const scrollDistance = Math.round('left' === direction || 'right' === direction ? 0.6 * width : 0.6 * height);
807
+ debugDevice(`Performing scroll: ${direction}, distance: ${scrollDistance}`);
808
+ switch(direction){
779
809
  case 'up':
780
- start = {
781
- x: Math.round(width / 2),
782
- y: Math.round(0.2 * height)
783
- };
810
+ await this.swipe(start.x, start.y, start.x, start.y + scrollDistance, 300);
784
811
  break;
785
812
  case 'down':
786
- start = {
787
- x: Math.round(width / 2),
788
- y: Math.round(0.8 * height)
789
- };
813
+ await this.swipe(start.x, start.y, start.x, start.y - scrollDistance, 300);
790
814
  break;
791
815
  case 'left':
792
- start = {
793
- x: Math.round(0.8 * width),
794
- y: Math.round(height / 2)
795
- };
816
+ await this.swipe(start.x, start.y, start.x + scrollDistance, start.y, 300);
796
817
  break;
797
818
  case 'right':
798
- start = {
799
- x: Math.round(0.2 * width),
800
- y: Math.round(height / 2)
801
- };
819
+ await this.swipe(start.x, start.y, start.x - scrollDistance, start.y, 300);
802
820
  break;
803
821
  }
804
- let lastScreenshot = null;
805
- let unchangedCount = 0;
806
- debugDevice(`Starting scroll to ${direction} with content detection`);
807
- for(let i = 0; i < maxAttempts; i++)try {
808
- debugDevice(`Scroll attempt ${i + 1}/${maxAttempts}`);
809
- await (0, utils_namespaceObject.sleep)(500);
810
- const currentScreenshot = await this.screenshotBase64();
811
- if (lastScreenshot && this.compareScreenshots(lastScreenshot, currentScreenshot, 10)) {
812
- unchangedCount++;
813
- debugDevice(`Screen content unchanged (${unchangedCount}/${maxUnchangedCount})`);
814
- if (unchangedCount >= maxUnchangedCount) {
815
- debugDevice(`Reached ${direction}: screen content no longer changes`);
816
- break;
817
- }
818
- } else {
819
- if (lastScreenshot) debugDevice(`Content changed, resetting counter (was ${unchangedCount})`);
820
- unchangedCount = 0;
821
- }
822
- if (i >= 15 && 0 === unchangedCount) {
823
- debugDevice(`Too many attempts with dynamic content, stopping scroll to ${direction}`);
824
- break;
825
- }
826
- lastScreenshot = currentScreenshot;
827
- const scrollDistance = Math.round('left' === direction || 'right' === direction ? 0.6 * width : 0.6 * height);
828
- debugDevice(`Performing scroll: ${direction}, distance: ${scrollDistance}`);
829
- switch(direction){
830
- case 'up':
831
- await this.swipe(start.x, start.y, start.x, start.y + scrollDistance, 300);
832
- break;
833
- case 'down':
834
- await this.swipe(start.x, start.y, start.x, start.y - scrollDistance, 300);
835
- break;
836
- case 'left':
837
- await this.swipe(start.x, start.y, start.x + scrollDistance, start.y, 300);
838
- break;
839
- case 'right':
840
- await this.swipe(start.x, start.y, start.x - scrollDistance, start.y, 300);
841
- break;
842
- }
843
- debugDevice('Waiting for scroll and inertia to complete...');
844
- await (0, utils_namespaceObject.sleep)(2000);
845
- } catch (error) {
846
- debugDevice(`Error during scroll attempt ${i + 1}: ${error}`);
847
- await (0, utils_namespaceObject.sleep)(300);
848
- }
849
- debugDevice(`Scroll to ${direction} completed after ${maxAttempts} attempts`);
850
- }
851
- async scrollUntilLeft(startPoint) {
852
- await this.scrollUntilBoundary('left', startPoint, 1);
853
- }
854
- async scrollUntilRight(startPoint) {
855
- await this.scrollUntilBoundary('right', startPoint, 3);
856
- }
857
- async home() {
858
- await this.wdaBackend.pressHomeButton();
822
+ debugDevice('Waiting for scroll and inertia to complete...');
823
+ await (0, utils_namespaceObject.sleep)(2000);
824
+ } catch (error) {
825
+ debugDevice(`Error during scroll attempt ${i + 1}: ${error}`);
826
+ await (0, utils_namespaceObject.sleep)(300);
859
827
  }
860
- async appSwitcher() {
861
- try {
862
- debugDevice('Triggering app switcher with slow swipe up gesture');
863
- const { width, height } = await this.size();
864
- const centerX = Math.round(width / 2);
865
- const startY = Math.round(height - 5);
866
- const endY = Math.round(0.5 * height);
867
- await this.wdaBackend.swipe(centerX, startY, centerX, endY, 1500);
868
- await (0, utils_namespaceObject.sleep)(800);
869
- } catch (error) {
870
- debugDevice(`App switcher failed: ${error}`);
871
- throw new Error(`Failed to trigger app switcher: ${error}`);
872
- }
828
+ debugDevice(`Scroll to ${direction} completed after ${maxAttempts} attempts`);
829
+ }
830
+ async scrollUntilLeft(startPoint) {
831
+ await this.scrollUntilBoundary('left', startPoint, 1);
832
+ }
833
+ async scrollUntilRight(startPoint) {
834
+ await this.scrollUntilBoundary('right', startPoint, 3);
835
+ }
836
+ async home() {
837
+ await this.wdaBackend.pressHomeButton();
838
+ }
839
+ async appSwitcher() {
840
+ try {
841
+ debugDevice('Triggering app switcher with slow swipe up gesture');
842
+ const { width, height } = await this.size();
843
+ const centerX = Math.round(width / 2);
844
+ const startY = Math.round(height - 5);
845
+ const endY = Math.round(0.5 * height);
846
+ await this.wdaBackend.swipe(centerX, startY, centerX, endY, 1500);
847
+ await (0, utils_namespaceObject.sleep)(800);
848
+ } catch (error) {
849
+ debugDevice(`App switcher failed: ${error}`);
850
+ throw new Error(`Failed to trigger app switcher: ${error}`);
873
851
  }
874
- async hideKeyboard(keyNames) {
852
+ }
853
+ async hideKeyboard(keyNames) {
854
+ try {
855
+ const dismissKeys = keyNames && keyNames.length > 0 ? keyNames : [
856
+ 'return',
857
+ 'done',
858
+ 'go',
859
+ 'search',
860
+ 'next',
861
+ 'send'
862
+ ];
863
+ debugDevice(`Attempting to dismiss keyboard using WDA API with keys: ${dismissKeys.join(', ')}`);
875
864
  try {
876
- const dismissKeys = keyNames && keyNames.length > 0 ? keyNames : [
877
- 'return',
878
- 'done',
879
- 'go',
880
- 'search',
881
- 'next',
882
- 'send'
883
- ];
884
- debugDevice(`Attempting to dismiss keyboard using WDA API with keys: ${dismissKeys.join(', ')}`);
885
- try {
886
- await this.wdaBackend.dismissKeyboard(dismissKeys);
887
- debugDevice('Successfully dismissed keyboard using WDA API');
888
- await (0, utils_namespaceObject.sleep)(500);
889
- return true;
890
- } catch (wdaError) {
891
- debugDevice(`WDA dismissKeyboard failed, falling back to swipe gesture: ${wdaError}`);
892
- }
893
- const windowSize = await this.wdaBackend.getWindowSize();
894
- const centerX = Math.round(windowSize.width / 2);
895
- const startY = Math.round(0.9 * windowSize.height);
896
- const endY = Math.round(0.5 * windowSize.height);
897
- await this.swipe(centerX, startY, centerX, endY, 300);
898
- debugDevice('Dismissed keyboard with swipe up gesture from bottom of screen');
865
+ await this.wdaBackend.dismissKeyboard(dismissKeys);
866
+ debugDevice('Successfully dismissed keyboard using WDA API');
899
867
  await (0, utils_namespaceObject.sleep)(500);
900
868
  return true;
901
- } catch (error) {
902
- debugDevice(`Failed to hide keyboard: ${error}`);
903
- return false;
869
+ } catch (wdaError) {
870
+ debugDevice(`WDA dismissKeyboard failed, falling back to swipe gesture: ${wdaError}`);
904
871
  }
872
+ const windowSize = await this.wdaBackend.getWindowSize();
873
+ const centerX = Math.round(windowSize.width / 2);
874
+ const startY = Math.round(0.9 * windowSize.height);
875
+ const endY = Math.round(0.5 * windowSize.height);
876
+ await this.swipe(centerX, startY, centerX, endY, 300);
877
+ debugDevice('Dismissed keyboard with swipe up gesture from bottom of screen');
878
+ await (0, utils_namespaceObject.sleep)(500);
879
+ return true;
880
+ } catch (error) {
881
+ debugDevice(`Failed to hide keyboard: ${error}`);
882
+ return false;
905
883
  }
906
- async openUrl(url, options) {
907
- const opts = {
908
- useSafariAsBackup: true,
909
- waitTime: 2000,
910
- ...options
911
- };
912
- try {
913
- debugDevice(`Opening URL: ${url}`);
914
- await this.wdaBackend.openUrl(url);
915
- await (0, utils_namespaceObject.sleep)(opts.waitTime);
916
- debugDevice(`Successfully opened URL: ${url}`);
917
- } catch (error) {
918
- debugDevice(`Direct URL opening failed: ${error}`);
919
- if (opts.useSafariAsBackup) {
920
- debugDevice(`Attempting to open URL via Safari: ${url}`);
921
- await this.openUrlViaSafari(url);
922
- } else throw new Error(`Failed to open URL: ${error}`);
923
- }
884
+ }
885
+ async openUrl(url, options) {
886
+ const opts = {
887
+ useSafariAsBackup: true,
888
+ waitTime: 2000,
889
+ ...options
890
+ };
891
+ try {
892
+ debugDevice(`Opening URL: ${url}`);
893
+ await this.wdaBackend.openUrl(url);
894
+ await (0, utils_namespaceObject.sleep)(opts.waitTime);
895
+ debugDevice(`Successfully opened URL: ${url}`);
896
+ } catch (error) {
897
+ debugDevice(`Direct URL opening failed: ${error}`);
898
+ if (opts.useSafariAsBackup) {
899
+ debugDevice(`Attempting to open URL via Safari: ${url}`);
900
+ await this.openUrlViaSafari(url);
901
+ } else throw new Error(`Failed to open URL: ${error}`);
924
902
  }
925
- async openUrlViaSafari(url) {
903
+ }
904
+ async openUrlViaSafari(url) {
905
+ try {
906
+ debugDevice(`Opening URL via Safari: ${url}`);
907
+ await this.wdaBackend.terminateApp('com.apple.mobilesafari');
908
+ await this.wdaBackend.launchApp('com.apple.mobilesafari');
909
+ await (0, utils_namespaceObject.sleep)(2000);
910
+ await this.typeText(url);
911
+ await (0, utils_namespaceObject.sleep)(500);
912
+ await this.pressKey('Return');
913
+ await (0, utils_namespaceObject.sleep)(1000);
926
914
  try {
927
- debugDevice(`Opening URL via Safari: ${url}`);
928
- await this.wdaBackend.terminateApp('com.apple.mobilesafari');
929
- await this.wdaBackend.launchApp('com.apple.mobilesafari');
930
915
  await (0, utils_namespaceObject.sleep)(2000);
931
- await this.typeText(url);
932
- await (0, utils_namespaceObject.sleep)(500);
933
- await this.pressKey('Return');
934
- await (0, utils_namespaceObject.sleep)(1000);
935
- try {
936
- await (0, utils_namespaceObject.sleep)(2000);
937
- debugDevice(`URL opened via Safari: ${url}`);
938
- } catch (dialogError) {
939
- debugDevice(`No confirmation dialog or dialog handling failed: ${dialogError}`);
940
- }
941
- } catch (error) {
942
- debugDevice(`Failed to open URL via Safari: ${error}`);
943
- throw new Error(`Failed to open URL via Safari: ${error}`);
944
- }
945
- }
946
- async runWdaRequest(method, endpoint, data) {
947
- return await this.wdaBackend.executeRequest(method, endpoint, data);
948
- }
949
- async destroy() {
950
- if (this.destroyed) return;
951
- try {
952
- await this.wdaBackend.deleteSession();
953
- await this.wdaManager.stop();
954
- } catch (error) {
955
- debugDevice(`Error during cleanup: ${error}`);
916
+ debugDevice(`URL opened via Safari: ${url}`);
917
+ } catch (dialogError) {
918
+ debugDevice(`No confirmation dialog or dialog handling failed: ${dialogError}`);
956
919
  }
957
- this.destroyed = true;
958
- debugDevice(`iOS device ${this.deviceId} destroyed`);
920
+ } catch (error) {
921
+ debugDevice(`Failed to open URL via Safari: ${error}`);
922
+ throw new Error(`Failed to open URL via Safari: ${error}`);
959
923
  }
960
- constructor(options){
961
- _define_property(this, "deviceId", void 0);
962
- _define_property(this, "devicePixelRatio", 1);
963
- _define_property(this, "devicePixelRatioInitialized", false);
964
- _define_property(this, "destroyed", false);
965
- _define_property(this, "description", void 0);
966
- _define_property(this, "customActions", void 0);
967
- _define_property(this, "wdaBackend", void 0);
968
- _define_property(this, "wdaManager", void 0);
969
- _define_property(this, "interfaceType", 'ios');
970
- _define_property(this, "uri", void 0);
971
- _define_property(this, "options", void 0);
972
- this.deviceId = 'pending-connection';
973
- this.options = options;
974
- this.customActions = null == options ? void 0 : options.customActions;
975
- const wdaPort = (null == options ? void 0 : options.wdaPort) || constants_namespaceObject.DEFAULT_WDA_PORT;
976
- const wdaHost = (null == options ? void 0 : options.wdaHost) || 'localhost';
977
- this.wdaBackend = new IOSWebDriverClient({
978
- port: wdaPort,
979
- host: wdaHost
980
- });
981
- this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
924
+ }
925
+ async runWdaRequest(method, endpoint, data) {
926
+ return await this.wdaBackend.executeRequest(method, endpoint, data);
927
+ }
928
+ async destroy() {
929
+ if (this.destroyed) return;
930
+ try {
931
+ await this.wdaBackend.deleteSession();
932
+ await this.wdaManager.stop();
933
+ } catch (error) {
934
+ debugDevice(`Error during cleanup: ${error}`);
982
935
  }
936
+ this.destroyed = true;
937
+ debugDevice(`iOS device ${this.deviceId} destroyed`);
983
938
  }
984
- const runWdaRequestParamSchema = core_namespaceObject.z.object({
985
- method: core_namespaceObject.z["enum"](WDA_HTTP_METHODS).describe('HTTP method (GET, POST, DELETE, PUT)'),
986
- endpoint: core_namespaceObject.z.string().describe('WebDriver API endpoint'),
987
- data: core_namespaceObject.z.object({}).passthrough().optional().describe('Optional request body data as JSON object')
988
- });
989
- const launchParamSchema = core_namespaceObject.z.string().describe('App bundle ID or URL to launch');
990
- const createPlatformActions = (device)=>({
991
- RunWdaRequest: (0, device_namespaceObject.defineAction)({
992
- name: 'RunWdaRequest',
993
- description: 'Execute WebDriverAgent API request directly on iOS device',
994
- interfaceAlias: 'runWdaRequest',
995
- paramSchema: runWdaRequestParamSchema,
996
- call: async (param)=>await device.runWdaRequest(param.method, param.endpoint, param.data)
997
- }),
998
- Launch: (0, device_namespaceObject.defineAction)({
999
- name: 'Launch',
1000
- description: 'Launch an iOS app or URL',
1001
- interfaceAlias: 'launch',
1002
- paramSchema: launchParamSchema,
1003
- call: async (param)=>{
1004
- await device.launch(param);
1005
- }
1006
- }),
1007
- IOSHomeButton: (0, device_namespaceObject.defineAction)({
1008
- name: 'IOSHomeButton',
1009
- description: 'Trigger the system "home" operation on iOS devices',
1010
- call: async ()=>{
1011
- await device.home();
1012
- }
1013
- }),
1014
- IOSAppSwitcher: (0, device_namespaceObject.defineAction)({
1015
- name: 'IOSAppSwitcher',
1016
- description: 'Trigger the system "app switcher" operation on iOS devices',
1017
- call: async ()=>{
1018
- await device.appSwitcher();
1019
- }
1020
- })
939
+ constructor(options){
940
+ _define_property(this, "deviceId", void 0);
941
+ _define_property(this, "devicePixelRatio", 1);
942
+ _define_property(this, "devicePixelRatioInitialized", false);
943
+ _define_property(this, "destroyed", false);
944
+ _define_property(this, "description", void 0);
945
+ _define_property(this, "customActions", void 0);
946
+ _define_property(this, "wdaBackend", void 0);
947
+ _define_property(this, "wdaManager", void 0);
948
+ _define_property(this, "interfaceType", 'ios');
949
+ _define_property(this, "uri", void 0);
950
+ _define_property(this, "options", void 0);
951
+ this.deviceId = 'pending-connection';
952
+ this.options = options;
953
+ this.customActions = options?.customActions;
954
+ const wdaPort = options?.wdaPort || constants_namespaceObject.DEFAULT_WDA_PORT;
955
+ const wdaHost = options?.wdaHost || 'localhost';
956
+ this.wdaBackend = new IOSWebDriverClient({
957
+ port: wdaPort,
958
+ host: wdaHost
1021
959
  });
1022
- const agent_namespaceObject = require("@midscene/core/agent");
1023
- const external_node_child_process_namespaceObject = require("node:child_process");
1024
- const external_node_os_namespaceObject = require("node:os");
1025
- const external_node_util_namespaceObject = require("node:util");
1026
- const execAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.exec);
1027
- const debugUtils = (0, logger_namespaceObject.getDebug)('ios:utils');
1028
- function checkMacOSPlatform() {
1029
- const currentPlatform = (0, external_node_os_namespaceObject.platform)();
1030
- return {
1031
- isMacOS: 'darwin' === currentPlatform,
1032
- platform: currentPlatform
1033
- };
960
+ this.wdaManager = webdriver_namespaceObject.WDAManager.getInstance(wdaPort, wdaHost);
1034
961
  }
1035
- async function checkIOSEnvironment() {
1036
- try {
1037
- const platformCheck = checkMacOSPlatform();
1038
- if (!platformCheck.isMacOS) return {
1039
- available: false,
1040
- error: `iOS development is only supported on macOS. Current platform: ${platformCheck.platform}`
1041
- };
1042
- const { stdout: xcrunPath } = await execAsync('which xcrun');
1043
- if (!xcrunPath.trim()) return {
1044
- available: false,
1045
- error: 'xcrun not found. Please install Xcode Command Line Tools: xcode-select --install'
1046
- };
1047
- try {
1048
- await execAsync('xcodebuild -version');
1049
- } catch (error) {
1050
- return {
1051
- available: false,
1052
- error: 'xcodebuild not found. Please install Xcode from the App Store'
1053
- };
962
+ }
963
+ const runWdaRequestParamSchema = core_namespaceObject.z.object({
964
+ method: core_namespaceObject.z["enum"](WDA_HTTP_METHODS).describe('HTTP method (GET, POST, DELETE, PUT)'),
965
+ endpoint: core_namespaceObject.z.string().describe('WebDriver API endpoint'),
966
+ data: core_namespaceObject.z.object({}).passthrough().optional().describe('Optional request body data as JSON object')
967
+ });
968
+ const launchParamSchema = core_namespaceObject.z.string().describe('App bundle ID or URL to launch');
969
+ const createPlatformActions = (device)=>({
970
+ RunWdaRequest: (0, device_namespaceObject.defineAction)({
971
+ name: 'RunWdaRequest',
972
+ description: 'Execute WebDriverAgent API request directly on iOS device',
973
+ interfaceAlias: 'runWdaRequest',
974
+ paramSchema: runWdaRequestParamSchema,
975
+ call: async (param)=>await device.runWdaRequest(param.method, param.endpoint, param.data)
976
+ }),
977
+ Launch: (0, device_namespaceObject.defineAction)({
978
+ name: 'Launch',
979
+ description: 'Launch an iOS app or URL',
980
+ interfaceAlias: 'launch',
981
+ paramSchema: launchParamSchema,
982
+ call: async (param)=>{
983
+ await device.launch(param);
1054
984
  }
1055
- debugUtils('iOS environment is available for WebDriverAgent');
1056
- return {
1057
- available: true
1058
- };
985
+ }),
986
+ IOSHomeButton: (0, device_namespaceObject.defineAction)({
987
+ name: 'IOSHomeButton',
988
+ description: 'Trigger the system "home" operation on iOS devices',
989
+ call: async ()=>{
990
+ await device.home();
991
+ }
992
+ }),
993
+ IOSAppSwitcher: (0, device_namespaceObject.defineAction)({
994
+ name: 'IOSAppSwitcher',
995
+ description: 'Trigger the system "app switcher" operation on iOS devices',
996
+ call: async ()=>{
997
+ await device.appSwitcher();
998
+ }
999
+ })
1000
+ });
1001
+ const agent_namespaceObject = require("@midscene/core/agent");
1002
+ const external_node_child_process_namespaceObject = require("node:child_process");
1003
+ const external_node_os_namespaceObject = require("node:os");
1004
+ const external_node_util_namespaceObject = require("node:util");
1005
+ const execAsync = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.exec);
1006
+ const debugUtils = (0, logger_namespaceObject.getDebug)('ios:utils');
1007
+ function checkMacOSPlatform() {
1008
+ const currentPlatform = (0, external_node_os_namespaceObject.platform)();
1009
+ return {
1010
+ isMacOS: 'darwin' === currentPlatform,
1011
+ platform: currentPlatform
1012
+ };
1013
+ }
1014
+ async function checkIOSEnvironment() {
1015
+ try {
1016
+ const platformCheck = checkMacOSPlatform();
1017
+ if (!platformCheck.isMacOS) return {
1018
+ available: false,
1019
+ error: `iOS development is only supported on macOS. Current platform: ${platformCheck.platform}`
1020
+ };
1021
+ const { stdout: xcrunPath } = await execAsync('which xcrun');
1022
+ if (!xcrunPath.trim()) return {
1023
+ available: false,
1024
+ error: 'xcrun not found. Please install Xcode Command Line Tools: xcode-select --install'
1025
+ };
1026
+ try {
1027
+ await execAsync('xcodebuild -version');
1059
1028
  } catch (error) {
1060
- const errorMsg = error instanceof Error ? error.message : String(error);
1061
- debugUtils(`iOS environment not available: ${errorMsg}`);
1062
- if (errorMsg.includes('xcrun')) return {
1063
- available: false,
1064
- error: 'Xcode Command Line Tools not properly configured. Please run: sudo xcode-select --reset'
1065
- };
1066
1029
  return {
1067
1030
  available: false,
1068
- error: `iOS development environment not available: ${errorMsg}`
1031
+ error: 'xcodebuild not found. Please install Xcode from the App Store'
1069
1032
  };
1070
1033
  }
1034
+ debugUtils('iOS environment is available for WebDriverAgent');
1035
+ return {
1036
+ available: true
1037
+ };
1038
+ } catch (error) {
1039
+ const errorMsg = error instanceof Error ? error.message : String(error);
1040
+ debugUtils(`iOS environment not available: ${errorMsg}`);
1041
+ if (errorMsg.includes('xcrun')) return {
1042
+ available: false,
1043
+ error: 'Xcode Command Line Tools not properly configured. Please run: sudo xcode-select --reset'
1044
+ };
1045
+ return {
1046
+ available: false,
1047
+ error: `iOS development environment not available: ${errorMsg}`
1048
+ };
1071
1049
  }
1072
- function agent_define_property(obj, key, value) {
1073
- if (key in obj) Object.defineProperty(obj, key, {
1074
- value: value,
1075
- enumerable: true,
1076
- configurable: true,
1077
- writable: true
1078
- });
1079
- else obj[key] = value;
1080
- return obj;
1081
- }
1082
- const debugAgent = (0, logger_namespaceObject.getDebug)('ios:agent');
1083
- class IOSAgent extends agent_namespaceObject.Agent {
1084
- createActionWrapper(name) {
1085
- const action = this.wrapActionInActionSpace(name);
1086
- return (...args)=>action(args[0]);
1087
- }
1088
- constructor(device, opts){
1089
- 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);
1090
- this.launch = this.createActionWrapper('Launch');
1091
- this.runWdaRequest = this.createActionWrapper('RunWdaRequest');
1092
- this.home = this.createActionWrapper('IOSHomeButton');
1093
- this.appSwitcher = this.createActionWrapper('IOSAppSwitcher');
1094
- }
1050
+ }
1051
+ function agent_define_property(obj, key, value) {
1052
+ if (key in obj) Object.defineProperty(obj, key, {
1053
+ value: value,
1054
+ enumerable: true,
1055
+ configurable: true,
1056
+ writable: true
1057
+ });
1058
+ else obj[key] = value;
1059
+ return obj;
1060
+ }
1061
+ const debugAgent = (0, logger_namespaceObject.getDebug)('ios:agent');
1062
+ class IOSAgent extends agent_namespaceObject.Agent {
1063
+ createActionWrapper(name) {
1064
+ const action = this.wrapActionInActionSpace(name);
1065
+ return (...args)=>action(args[0]);
1095
1066
  }
1096
- async function agentFromWebDriverAgent(opts) {
1097
- debugAgent('Creating iOS agent with WebDriverAgent auto-detection');
1098
- const envCheck = await checkIOSEnvironment();
1099
- if (!envCheck.available) throw new Error(`iOS environment not available: ${envCheck.error}`);
1100
- const device = new IOSDevice(opts || {});
1101
- await device.connect();
1102
- return new IOSAgent(device, opts);
1067
+ constructor(device, opts){
1068
+ 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);
1069
+ this.launch = this.createActionWrapper('Launch');
1070
+ this.runWdaRequest = this.createActionWrapper('RunWdaRequest');
1071
+ this.home = this.createActionWrapper('IOSHomeButton');
1072
+ this.appSwitcher = this.createActionWrapper('IOSAppSwitcher');
1103
1073
  }
1104
- const env_namespaceObject = require("@midscene/shared/env");
1105
- })();
1074
+ }
1075
+ async function agentFromWebDriverAgent(opts) {
1076
+ debugAgent('Creating iOS agent with WebDriverAgent auto-detection');
1077
+ const envCheck = await checkIOSEnvironment();
1078
+ if (!envCheck.available) throw new Error(`iOS environment not available: ${envCheck.error}`);
1079
+ const device = new IOSDevice(opts || {});
1080
+ await device.connect();
1081
+ return new IOSAgent(device, opts);
1082
+ }
1083
+ const env_namespaceObject = require("@midscene/shared/env");
1106
1084
  exports.IOSAgent = __webpack_exports__.IOSAgent;
1107
1085
  exports.IOSDevice = __webpack_exports__.IOSDevice;
1108
1086
  exports.IOSWebDriverClient = __webpack_exports__.IOSWebDriverClient;