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