playwright-automation-core 1.0.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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/dist/api/api.assertions.d.ts +196 -0
  4. package/dist/api/api.assertions.d.ts.map +1 -0
  5. package/dist/api/api.assertions.js +462 -0
  6. package/dist/api/api.assertions.js.map +1 -0
  7. package/dist/api/api.builder.d.ts +134 -0
  8. package/dist/api/api.builder.d.ts.map +1 -0
  9. package/dist/api/api.builder.js +273 -0
  10. package/dist/api/api.builder.js.map +1 -0
  11. package/dist/api/api.client.d.ts +157 -0
  12. package/dist/api/api.client.d.ts.map +1 -0
  13. package/dist/api/api.client.js +493 -0
  14. package/dist/api/api.client.js.map +1 -0
  15. package/dist/api/index.d.ts +8 -0
  16. package/dist/api/index.d.ts.map +1 -0
  17. package/dist/api/index.js +17 -0
  18. package/dist/api/index.js.map +1 -0
  19. package/dist/core/base.driver.d.ts +130 -0
  20. package/dist/core/base.driver.d.ts.map +1 -0
  21. package/dist/core/base.driver.js +264 -0
  22. package/dist/core/base.driver.js.map +1 -0
  23. package/dist/core/driver.factory.d.ts +120 -0
  24. package/dist/core/driver.factory.d.ts.map +1 -0
  25. package/dist/core/driver.factory.js +222 -0
  26. package/dist/core/driver.factory.js.map +1 -0
  27. package/dist/core/element.actions.d.ts +119 -0
  28. package/dist/core/element.actions.d.ts.map +1 -0
  29. package/dist/core/element.actions.js +379 -0
  30. package/dist/core/element.actions.js.map +1 -0
  31. package/dist/core/hook.manager.d.ts +186 -0
  32. package/dist/core/hook.manager.d.ts.map +1 -0
  33. package/dist/core/hook.manager.js +297 -0
  34. package/dist/core/hook.manager.js.map +1 -0
  35. package/dist/core/index.d.ts +8 -0
  36. package/dist/core/index.d.ts.map +1 -0
  37. package/dist/core/index.js +22 -0
  38. package/dist/core/index.js.map +1 -0
  39. package/dist/index.d.ts +17 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +128 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/mobile/index.d.ts +7 -0
  44. package/dist/mobile/index.d.ts.map +1 -0
  45. package/dist/mobile/index.js +14 -0
  46. package/dist/mobile/index.js.map +1 -0
  47. package/dist/mobile/mobile.assertions.d.ts +146 -0
  48. package/dist/mobile/mobile.assertions.d.ts.map +1 -0
  49. package/dist/mobile/mobile.assertions.js +333 -0
  50. package/dist/mobile/mobile.assertions.js.map +1 -0
  51. package/dist/mobile/mobile.driver.d.ts +297 -0
  52. package/dist/mobile/mobile.driver.d.ts.map +1 -0
  53. package/dist/mobile/mobile.driver.js +808 -0
  54. package/dist/mobile/mobile.driver.js.map +1 -0
  55. package/dist/types/common.types.d.ts +200 -0
  56. package/dist/types/common.types.d.ts.map +1 -0
  57. package/dist/types/common.types.js +3 -0
  58. package/dist/types/common.types.js.map +1 -0
  59. package/dist/types/config.types.d.ts +192 -0
  60. package/dist/types/config.types.d.ts.map +1 -0
  61. package/dist/types/config.types.js +3 -0
  62. package/dist/types/config.types.js.map +1 -0
  63. package/dist/types/driver.types.d.ts +500 -0
  64. package/dist/types/driver.types.d.ts.map +1 -0
  65. package/dist/types/driver.types.js +3 -0
  66. package/dist/types/driver.types.js.map +1 -0
  67. package/dist/types/enums.d.ts +53 -0
  68. package/dist/types/enums.d.ts.map +1 -0
  69. package/dist/types/enums.js +62 -0
  70. package/dist/types/enums.js.map +1 -0
  71. package/dist/types/index.d.ts +8 -0
  72. package/dist/types/index.d.ts.map +1 -0
  73. package/dist/types/index.js +15 -0
  74. package/dist/types/index.js.map +1 -0
  75. package/dist/utils/config.manager.d.ts +144 -0
  76. package/dist/utils/config.manager.d.ts.map +1 -0
  77. package/dist/utils/config.manager.js +473 -0
  78. package/dist/utils/config.manager.js.map +1 -0
  79. package/dist/utils/errors.d.ts +149 -0
  80. package/dist/utils/errors.d.ts.map +1 -0
  81. package/dist/utils/errors.js +290 -0
  82. package/dist/utils/errors.js.map +1 -0
  83. package/dist/utils/helpers.d.ts +124 -0
  84. package/dist/utils/helpers.d.ts.map +1 -0
  85. package/dist/utils/helpers.js +421 -0
  86. package/dist/utils/helpers.js.map +1 -0
  87. package/dist/utils/index.d.ts +9 -0
  88. package/dist/utils/index.d.ts.map +1 -0
  89. package/dist/utils/index.js +78 -0
  90. package/dist/utils/index.js.map +1 -0
  91. package/dist/utils/logger.d.ts +145 -0
  92. package/dist/utils/logger.d.ts.map +1 -0
  93. package/dist/utils/logger.js +235 -0
  94. package/dist/utils/logger.js.map +1 -0
  95. package/dist/utils/wait.utils.d.ts +95 -0
  96. package/dist/utils/wait.utils.d.ts.map +1 -0
  97. package/dist/utils/wait.utils.js +306 -0
  98. package/dist/utils/wait.utils.js.map +1 -0
  99. package/dist/web/index.d.ts +8 -0
  100. package/dist/web/index.d.ts.map +1 -0
  101. package/dist/web/index.js +16 -0
  102. package/dist/web/index.js.map +1 -0
  103. package/dist/web/web.assertions.d.ts +164 -0
  104. package/dist/web/web.assertions.d.ts.map +1 -0
  105. package/dist/web/web.assertions.js +427 -0
  106. package/dist/web/web.assertions.js.map +1 -0
  107. package/dist/web/web.driver.d.ts +174 -0
  108. package/dist/web/web.driver.d.ts.map +1 -0
  109. package/dist/web/web.driver.js +571 -0
  110. package/dist/web/web.driver.js.map +1 -0
  111. package/dist/web/web.page.d.ts +286 -0
  112. package/dist/web/web.page.d.ts.map +1 -0
  113. package/dist/web/web.page.js +570 -0
  114. package/dist/web/web.page.js.map +1 -0
  115. package/package.json +115 -0
@@ -0,0 +1,808 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MobileDriver = exports.AppState = void 0;
4
+ const webdriverio_1 = require("webdriverio");
5
+ const types_1 = require("../types");
6
+ const base_driver_1 = require("../core/base.driver");
7
+ const hook_manager_1 = require("../core/hook.manager");
8
+ const utils_1 = require("../utils");
9
+ /**
10
+ * App state enum
11
+ */
12
+ var AppState;
13
+ (function (AppState) {
14
+ AppState[AppState["NOT_INSTALLED"] = 0] = "NOT_INSTALLED";
15
+ AppState[AppState["NOT_RUNNING"] = 1] = "NOT_RUNNING";
16
+ AppState[AppState["RUNNING_BACKGROUND_SUSPENDED"] = 2] = "RUNNING_BACKGROUND_SUSPENDED";
17
+ AppState[AppState["RUNNING_BACKGROUND"] = 3] = "RUNNING_BACKGROUND";
18
+ AppState[AppState["RUNNING_FOREGROUND"] = 4] = "RUNNING_FOREGROUND";
19
+ })(AppState || (exports.AppState = AppState = {}));
20
+ /**
21
+ * Mobile Driver implementation using WebdriverIO with Appium.
22
+ * Supports both Android and iOS platforms.
23
+ */
24
+ class MobileDriver extends base_driver_1.BaseDriver {
25
+ /** WebdriverIO browser/driver instance */
26
+ driver = null;
27
+ /** Mobile-specific configuration */
28
+ config;
29
+ /** Hook manager */
30
+ hooks;
31
+ /** Current context (NATIVE_APP or WEBVIEW_xxx) */
32
+ currentContext = "NATIVE_APP";
33
+ /**
34
+ * Create a new MobileDriver instance
35
+ */
36
+ constructor(config, logger) {
37
+ super(types_1.Platform.MOBILE, config, logger);
38
+ this.config = config;
39
+ this.hooks = (0, hook_manager_1.getHookManager)();
40
+ }
41
+ /**
42
+ * Initialize the mobile driver
43
+ */
44
+ async initialize() {
45
+ if (this.initialized) {
46
+ this.logger.warn("MobileDriver already initialized");
47
+ return;
48
+ }
49
+ // Execute before init hooks
50
+ await this.hooks.execute(hook_manager_1.HookEvent.BEFORE_DRIVER_INIT, {
51
+ platform: this.platform,
52
+ data: { config: this.getSafeConfig() },
53
+ });
54
+ try {
55
+ this.logger.info("Initializing MobileDriver", {
56
+ mobilePlatform: this.config.platform,
57
+ deviceName: this.config.deviceName,
58
+ });
59
+ // Build capabilities
60
+ const capabilities = this.buildCapabilities();
61
+ // Create WebdriverIO remote session
62
+ this.driver = await (0, webdriverio_1.remote)({
63
+ hostname: this.config.appiumHost || "localhost",
64
+ port: this.config.appiumPort || 4723,
65
+ path: this.config.appiumPath || "/wd/hub",
66
+ capabilities,
67
+ logLevel: "warn",
68
+ connectionRetryTimeout: this.config.timeout || 60000,
69
+ connectionRetryCount: 3,
70
+ });
71
+ // Generate session ID and mark initialized
72
+ this.generateSessionId();
73
+ this.markInitialized();
74
+ // Execute after init hooks
75
+ await this.hooks.execute(hook_manager_1.HookEvent.AFTER_DRIVER_INIT, {
76
+ platform: this.platform,
77
+ driverStatus: this.getStatus(),
78
+ data: {
79
+ mobilePlatform: this.config.platform,
80
+ deviceName: this.config.deviceName,
81
+ },
82
+ });
83
+ }
84
+ catch (error) {
85
+ const err = error;
86
+ this.logger.error("Failed to initialize MobileDriver", {
87
+ error: err.message,
88
+ });
89
+ throw new utils_1.DriverInitializationError(this.platform, `Failed to initialize mobile driver: ${err.message}`, {
90
+ mobilePlatform: this.config.platform,
91
+ deviceName: this.config.deviceName,
92
+ });
93
+ }
94
+ }
95
+ /**
96
+ * Build Appium capabilities from configuration
97
+ */
98
+ buildCapabilities() {
99
+ const isIOS = this.config.platform === types_1.MobilePlatform.IOS;
100
+ const caps = {
101
+ platformName: isIOS ? "iOS" : "Android",
102
+ "appium:deviceName": this.config.deviceName,
103
+ "appium:automationName": this.config.automationName || (isIOS ? "XCUITest" : "UiAutomator2"),
104
+ "appium:newCommandTimeout": this.config.newCommandTimeout || 300,
105
+ };
106
+ // Add app capability
107
+ if (this.config.app) {
108
+ caps["appium:app"] = this.config.app;
109
+ }
110
+ // Platform-specific capabilities
111
+ if (isIOS) {
112
+ if (this.config.platformVersion) {
113
+ caps["appium:platformVersion"] = this.config.platformVersion;
114
+ }
115
+ if (this.config.udid) {
116
+ caps["appium:udid"] = this.config.udid;
117
+ }
118
+ if (this.config.bundleId) {
119
+ caps["appium:bundleId"] = this.config.bundleId;
120
+ }
121
+ }
122
+ else {
123
+ // Android
124
+ if (this.config.appPackage) {
125
+ caps["appium:appPackage"] = this.config.appPackage;
126
+ }
127
+ if (this.config.appActivity) {
128
+ caps["appium:appActivity"] = this.config.appActivity;
129
+ }
130
+ if (this.config.avd) {
131
+ caps["appium:avd"] = this.config.avd;
132
+ }
133
+ }
134
+ // Additional capabilities
135
+ if (this.config.noReset !== undefined) {
136
+ caps["appium:noReset"] = this.config.noReset;
137
+ }
138
+ if (this.config.fullReset !== undefined) {
139
+ caps["appium:fullReset"] = this.config.fullReset;
140
+ }
141
+ // Merge additional capabilities
142
+ if (this.config.additionalCapabilities) {
143
+ Object.assign(caps, this.config.additionalCapabilities);
144
+ }
145
+ return caps;
146
+ }
147
+ /**
148
+ * Get the underlying WebdriverIO driver
149
+ */
150
+ getDriver() {
151
+ this.ensureInitialized("getDriver");
152
+ return this.driver;
153
+ }
154
+ /**
155
+ * Get browser (alias for getDriver to match interface)
156
+ */
157
+ getBrowser() {
158
+ return this.getDriver();
159
+ }
160
+ // ============ Element Finding ============
161
+ /**
162
+ * Find element using locator
163
+ */
164
+ async findElement(locator) {
165
+ this.ensureInitialized("findElement");
166
+ const selector = this.buildSelector(locator);
167
+ return await this.driver.$(selector);
168
+ }
169
+ /**
170
+ * Find multiple elements using locator
171
+ */
172
+ async findElements(locator) {
173
+ this.ensureInitialized("findElements");
174
+ const selector = this.buildSelector(locator);
175
+ const elements = await this.driver.$$(selector);
176
+ // Convert ElementArray to regular array
177
+ return Array.from(elements);
178
+ }
179
+ /**
180
+ * Build selector string from locator
181
+ */
182
+ buildSelector(locator) {
183
+ if (typeof locator === "string") {
184
+ return locator;
185
+ }
186
+ const isIOS = this.config.platform === types_1.MobilePlatform.IOS;
187
+ switch (locator.strategy) {
188
+ case "accessibility":
189
+ return `~${locator.value}`;
190
+ case "id":
191
+ return isIOS
192
+ ? `~${locator.value}`
193
+ : `android=new UiSelector().resourceId("${locator.value}")`;
194
+ case "xpath":
195
+ return locator.value;
196
+ case "class":
197
+ return isIOS
198
+ ? `-ios class chain:**/${locator.value}`
199
+ : `android=new UiSelector().className("${locator.value}")`;
200
+ case "name":
201
+ return isIOS
202
+ ? `-ios predicate string:name == "${locator.value}"`
203
+ : `android=new UiSelector().text("${locator.value}")`;
204
+ case "text":
205
+ return isIOS
206
+ ? `-ios predicate string:label == "${locator.value}"`
207
+ : `android=new UiSelector().text("${locator.value}")`;
208
+ default:
209
+ return locator.value;
210
+ }
211
+ }
212
+ // ============ Element Actions ============
213
+ /**
214
+ * Click on element
215
+ */
216
+ async click(locator, _options) {
217
+ this.ensureInitialized("click");
218
+ const description = typeof locator === "string" ? locator : locator.value;
219
+ this.logger.elementAction("click", description);
220
+ const element = await this.findElement(locator);
221
+ await element.click();
222
+ }
223
+ /**
224
+ * Tap at coordinates
225
+ */
226
+ async tap(x, y) {
227
+ this.ensureInitialized("tap");
228
+ this.logger.debug(`Tap at coordinates: (${x}, ${y})`);
229
+ await this.driver.action("pointer", {
230
+ parameters: { pointerType: "touch" },
231
+ })
232
+ .move({ x, y })
233
+ .down()
234
+ .up()
235
+ .perform();
236
+ }
237
+ /**
238
+ * Tap on element (convenience method)
239
+ */
240
+ async tapElement(locator) {
241
+ await this.click(locator);
242
+ }
243
+ /**
244
+ * Double tap on element
245
+ */
246
+ async doubleTap(locator) {
247
+ this.ensureInitialized("doubleTap");
248
+ const description = typeof locator === "string" ? locator : locator.value;
249
+ this.logger.elementAction("doubleTap", description);
250
+ const element = await this.findElement(locator);
251
+ await element.doubleClick();
252
+ }
253
+ /**
254
+ * Long press on element
255
+ */
256
+ async longPress(locator, duration = 1000) {
257
+ this.ensureInitialized("longPress");
258
+ const description = typeof locator === "string" ? locator : locator.value;
259
+ this.logger.elementAction("longPress", description);
260
+ const element = await this.findElement(locator);
261
+ const location = await element.getLocation();
262
+ const size = await element.getSize();
263
+ const centerX = location.x + size.width / 2;
264
+ const centerY = location.y + size.height / 2;
265
+ await this.driver.action("pointer", {
266
+ parameters: { pointerType: "touch" },
267
+ })
268
+ .move({ x: Math.round(centerX), y: Math.round(centerY) })
269
+ .down()
270
+ .pause(duration)
271
+ .up()
272
+ .perform();
273
+ }
274
+ // ============ Swipe Actions ============
275
+ /**
276
+ * Swipe in direction
277
+ */
278
+ async swipe(direction, options) {
279
+ this.ensureInitialized("swipe");
280
+ this.logger.debug(`Swipe ${direction}`);
281
+ const { width, height } = await this.getWindowSize();
282
+ const duration = options?.duration || 300;
283
+ let startX, startY, endX, endY;
284
+ switch (direction) {
285
+ case "up":
286
+ startX = Math.round(width / 2);
287
+ startY = Math.round(height * 0.7);
288
+ endX = Math.round(width / 2);
289
+ endY = Math.round(height * 0.3);
290
+ break;
291
+ case "down":
292
+ startX = Math.round(width / 2);
293
+ startY = Math.round(height * 0.3);
294
+ endX = Math.round(width / 2);
295
+ endY = Math.round(height * 0.7);
296
+ break;
297
+ case "left":
298
+ startX = Math.round(width * 0.8);
299
+ startY = Math.round(height / 2);
300
+ endX = Math.round(width * 0.2);
301
+ endY = Math.round(height / 2);
302
+ break;
303
+ case "right":
304
+ startX = Math.round(width * 0.2);
305
+ startY = Math.round(height / 2);
306
+ endX = Math.round(width * 0.8);
307
+ endY = Math.round(height / 2);
308
+ break;
309
+ }
310
+ await this.driver.action("pointer", {
311
+ parameters: { pointerType: "touch" },
312
+ })
313
+ .move({ x: startX, y: startY })
314
+ .down()
315
+ .pause(50)
316
+ .move({ x: endX, y: endY, duration })
317
+ .up()
318
+ .perform();
319
+ }
320
+ /**
321
+ * Swipe with specific coordinates
322
+ */
323
+ async swipeCoordinates(startX, startY, endX, endY, duration = 300) {
324
+ this.ensureInitialized("swipeCoordinates");
325
+ await this.driver.action("pointer", {
326
+ parameters: { pointerType: "touch" },
327
+ })
328
+ .move({ x: startX, y: startY })
329
+ .down()
330
+ .pause(50)
331
+ .move({ x: endX, y: endY, duration })
332
+ .up()
333
+ .perform();
334
+ }
335
+ /**
336
+ * Scroll to element
337
+ */
338
+ async scrollToElement(locator) {
339
+ this.ensureInitialized("scrollToElement");
340
+ const description = typeof locator === "string" ? locator : locator.value;
341
+ this.logger.debug(`Scrolling to element: ${description}`);
342
+ const maxScrolls = 5;
343
+ for (let i = 0; i < maxScrolls; i++) {
344
+ try {
345
+ const element = await this.findElement(locator);
346
+ if (await element.isDisplayed()) {
347
+ return;
348
+ }
349
+ }
350
+ catch {
351
+ // Element not found, continue scrolling
352
+ }
353
+ await this.swipe("up");
354
+ }
355
+ throw new utils_1.MobileOperationError("scrollToElement", types_1.Platform.MOBILE, `Element not found after ${maxScrolls} scrolls: ${description}`, { locator: description });
356
+ }
357
+ // ============ Input Actions ============
358
+ /**
359
+ * Type text into element
360
+ */
361
+ async type(locator, text, _options) {
362
+ this.ensureInitialized("type");
363
+ const description = typeof locator === "string" ? locator : locator.value;
364
+ this.logger.elementAction("type", description);
365
+ const element = await this.findElement(locator);
366
+ await element.setValue(text);
367
+ }
368
+ /**
369
+ * Clear element text
370
+ */
371
+ async clear(locator, _options) {
372
+ this.ensureInitialized("clear");
373
+ const description = typeof locator === "string" ? locator : locator.value;
374
+ this.logger.elementAction("clear", description);
375
+ const element = await this.findElement(locator);
376
+ await element.clearValue();
377
+ }
378
+ /**
379
+ * Get element text
380
+ */
381
+ async getText(locator, _options) {
382
+ this.ensureInitialized("getText");
383
+ const element = await this.findElement(locator);
384
+ return await element.getText();
385
+ }
386
+ /**
387
+ * Get element attribute
388
+ */
389
+ async getAttribute(locator, attribute) {
390
+ this.ensureInitialized("getAttribute");
391
+ const element = await this.findElement(locator);
392
+ return await element.getAttribute(attribute);
393
+ }
394
+ /**
395
+ * Check if element is displayed
396
+ */
397
+ async isDisplayed(locator) {
398
+ this.ensureInitialized("isDisplayed");
399
+ try {
400
+ const element = await this.findElement(locator);
401
+ return await element.isDisplayed();
402
+ }
403
+ catch {
404
+ return false;
405
+ }
406
+ }
407
+ /**
408
+ * Check if element is enabled
409
+ */
410
+ async isEnabled(locator) {
411
+ this.ensureInitialized("isEnabled");
412
+ try {
413
+ const element = await this.findElement(locator);
414
+ return await element.isEnabled();
415
+ }
416
+ catch {
417
+ return false;
418
+ }
419
+ }
420
+ /**
421
+ * Wait for element to be visible
422
+ */
423
+ async waitForVisible(locator, options) {
424
+ this.ensureInitialized("waitForVisible");
425
+ const element = await this.findElement(locator);
426
+ await element.waitForDisplayed({
427
+ timeout: options?.timeout || this.getTimeout(),
428
+ });
429
+ }
430
+ /**
431
+ * Wait for element to be clickable
432
+ */
433
+ async waitForClickable(locator, options) {
434
+ this.ensureInitialized("waitForClickable");
435
+ const element = await this.findElement(locator);
436
+ await element.waitForClickable({
437
+ timeout: options?.timeout || this.getTimeout(),
438
+ });
439
+ }
440
+ /**
441
+ * Wait for element to be displayed (same as waitForVisible)
442
+ */
443
+ async waitForDisplayed(locator, timeout) {
444
+ this.ensureInitialized("waitForDisplayed");
445
+ const element = await this.findElement(locator);
446
+ await element.waitForDisplayed({
447
+ timeout: timeout || this.getTimeout(),
448
+ });
449
+ return element;
450
+ }
451
+ /**
452
+ * Wait for element to not be displayed
453
+ */
454
+ async waitForNotDisplayed(locator, timeout) {
455
+ this.ensureInitialized("waitForNotDisplayed");
456
+ const element = await this.findElement(locator);
457
+ await element.waitForDisplayed({
458
+ timeout: timeout || this.getTimeout(),
459
+ reverse: true,
460
+ });
461
+ }
462
+ // ============ Context Handling ============
463
+ /**
464
+ * Get available contexts
465
+ */
466
+ async getContexts() {
467
+ this.ensureInitialized("getContexts");
468
+ return (await this.driver.getContexts());
469
+ }
470
+ /**
471
+ * Get current context
472
+ */
473
+ async getContext() {
474
+ this.ensureInitialized("getContext");
475
+ this.currentContext = (await this.driver.getContext());
476
+ return this.currentContext;
477
+ }
478
+ /**
479
+ * Switch to context
480
+ */
481
+ async switchContext(context) {
482
+ this.ensureInitialized("switchContext");
483
+ this.logger.debug(`Switching to context: ${context}`);
484
+ await this.driver.switchContext(context);
485
+ this.currentContext = context;
486
+ }
487
+ /**
488
+ * Switch to native context
489
+ */
490
+ async switchToNative() {
491
+ await this.switchContext("NATIVE_APP");
492
+ }
493
+ /**
494
+ * Switch to webview context
495
+ */
496
+ async switchToWebview() {
497
+ this.ensureInitialized("switchToWebview");
498
+ const contexts = await this.getContexts();
499
+ const webviews = contexts.filter((c) => c.includes("WEBVIEW"));
500
+ if (webviews.length === 0) {
501
+ throw new utils_1.MobileOperationError("switchToWebview", types_1.Platform.MOBILE, "No webview contexts available");
502
+ }
503
+ await this.switchContext(webviews[0]);
504
+ }
505
+ // ============ App Management ============
506
+ /**
507
+ * Install app
508
+ */
509
+ async installApp(appPath) {
510
+ this.ensureInitialized("installApp");
511
+ this.logger.info(`Installing app: ${appPath}`);
512
+ await this.driver.installApp(appPath);
513
+ }
514
+ /**
515
+ * Remove app
516
+ */
517
+ async removeApp(appId) {
518
+ this.ensureInitialized("removeApp");
519
+ this.logger.info(`Removing app: ${appId}`);
520
+ await this.driver.removeApp(appId);
521
+ }
522
+ /**
523
+ * Check if app is installed
524
+ */
525
+ async isAppInstalled(appId) {
526
+ this.ensureInitialized("isAppInstalled");
527
+ return await this.driver.isAppInstalled(appId);
528
+ }
529
+ /**
530
+ * Launch app
531
+ */
532
+ async launchApp() {
533
+ this.ensureInitialized("launchApp");
534
+ this.logger.info("Launching app");
535
+ await this.driver.execute("mobile: launchApp", {});
536
+ }
537
+ /**
538
+ * Close/terminate app
539
+ */
540
+ async closeApp() {
541
+ this.ensureInitialized("closeApp");
542
+ const appId = this.config.appPackage || this.config.bundleId;
543
+ if (!appId) {
544
+ throw new utils_1.MobileOperationError("closeApp", types_1.Platform.MOBILE, "App ID not provided");
545
+ }
546
+ this.logger.info(`Closing app: ${appId}`);
547
+ await this.driver.terminateApp(appId, {});
548
+ }
549
+ /**
550
+ * Terminate app (alias for closeApp)
551
+ */
552
+ async terminateApp(appId) {
553
+ this.ensureInitialized("terminateApp");
554
+ const id = appId || this.config.appPackage || this.config.bundleId;
555
+ if (!id) {
556
+ throw new utils_1.MobileOperationError("terminateApp", types_1.Platform.MOBILE, "App ID not provided");
557
+ }
558
+ this.logger.info(`Terminating app: ${id}`);
559
+ await this.driver.terminateApp(id, {});
560
+ }
561
+ /**
562
+ * Activate app (bring to foreground)
563
+ */
564
+ async activateApp(appId) {
565
+ this.ensureInitialized("activateApp");
566
+ const id = appId || this.config.appPackage || this.config.bundleId;
567
+ if (!id) {
568
+ throw new utils_1.MobileOperationError("activateApp", types_1.Platform.MOBILE, "App ID not provided");
569
+ }
570
+ this.logger.info(`Activating app: ${id}`);
571
+ await this.driver.activateApp(id);
572
+ }
573
+ /**
574
+ * Get app state
575
+ */
576
+ async getAppState(appId) {
577
+ this.ensureInitialized("getAppState");
578
+ const id = appId || this.config.appPackage || this.config.bundleId;
579
+ if (!id) {
580
+ throw new utils_1.MobileOperationError("getAppState", types_1.Platform.MOBILE, "App ID not provided");
581
+ }
582
+ return (await this.driver.queryAppState(id));
583
+ }
584
+ /**
585
+ * Background app for duration
586
+ */
587
+ async backgroundApp(seconds = -1) {
588
+ this.ensureInitialized("backgroundApp");
589
+ this.logger.info(`Backgrounding app for ${seconds} seconds`);
590
+ await this.driver.background(seconds);
591
+ }
592
+ /**
593
+ * Reset app (clear data)
594
+ */
595
+ async resetApp() {
596
+ this.ensureInitialized("resetApp");
597
+ this.logger.info("Resetting app");
598
+ const appId = this.config.appPackage || this.config.bundleId;
599
+ if (appId) {
600
+ await this.terminateApp(appId);
601
+ await this.activateApp(appId);
602
+ }
603
+ }
604
+ // ============ Device Actions ============
605
+ /**
606
+ * Get window/screen size
607
+ */
608
+ async getWindowSize() {
609
+ this.ensureInitialized("getWindowSize");
610
+ return await this.driver.getWindowSize();
611
+ }
612
+ /**
613
+ * Get device orientation
614
+ */
615
+ async getOrientation() {
616
+ this.ensureInitialized("getOrientation");
617
+ const orientation = await this.driver.getOrientation();
618
+ return orientation.toUpperCase();
619
+ }
620
+ /**
621
+ * Set device orientation
622
+ */
623
+ async setOrientation(orientation) {
624
+ this.ensureInitialized("setOrientation");
625
+ this.logger.debug(`Setting orientation: ${orientation}`);
626
+ await this.driver.setOrientation(orientation);
627
+ }
628
+ /**
629
+ * Rotate device
630
+ */
631
+ async rotate() {
632
+ this.ensureInitialized("rotate");
633
+ const current = await this.getOrientation();
634
+ const next = current === "PORTRAIT" ? "LANDSCAPE" : "PORTRAIT";
635
+ await this.setOrientation(next);
636
+ }
637
+ /**
638
+ * Lock device
639
+ */
640
+ async lock(seconds) {
641
+ this.ensureInitialized("lock");
642
+ this.logger.debug("Locking device");
643
+ await this.driver.lock(seconds);
644
+ }
645
+ /**
646
+ * Unlock device
647
+ */
648
+ async unlock() {
649
+ this.ensureInitialized("unlock");
650
+ this.logger.debug("Unlocking device");
651
+ await this.driver.unlock();
652
+ }
653
+ /**
654
+ * Check if device is locked
655
+ */
656
+ async isLocked() {
657
+ this.ensureInitialized("isLocked");
658
+ return await this.driver.isLocked();
659
+ }
660
+ /**
661
+ * Press hardware back button (Android only)
662
+ */
663
+ async back() {
664
+ this.ensureInitialized("back");
665
+ if (this.config.platform !== types_1.MobilePlatform.ANDROID) {
666
+ this.logger.warn("Back button is only available on Android");
667
+ return;
668
+ }
669
+ await this.driver.back();
670
+ }
671
+ /**
672
+ * Press home button
673
+ */
674
+ async home() {
675
+ this.ensureInitialized("home");
676
+ if (this.config.platform === types_1.MobilePlatform.ANDROID) {
677
+ await this.driver.execute("mobile: pressKey", { keycode: 3 });
678
+ }
679
+ else {
680
+ await this.driver.execute("mobile: pressButton", { name: "home" });
681
+ }
682
+ }
683
+ /**
684
+ * Hide keyboard
685
+ */
686
+ async hideKeyboard() {
687
+ this.ensureInitialized("hideKeyboard");
688
+ try {
689
+ await this.driver.hideKeyboard();
690
+ }
691
+ catch {
692
+ // Keyboard might not be visible
693
+ this.logger.debug("Keyboard was not visible");
694
+ }
695
+ }
696
+ /**
697
+ * Check if keyboard is shown
698
+ */
699
+ async isKeyboardShown() {
700
+ this.ensureInitialized("isKeyboardShown");
701
+ return await this.driver.isKeyboardShown();
702
+ }
703
+ // ============ Additional Device Methods ============
704
+ /**
705
+ * Get device time
706
+ */
707
+ async getDeviceTime() {
708
+ this.ensureInitialized("getDeviceTime");
709
+ return (await this.driver.execute("mobile: getDeviceTime", {}));
710
+ }
711
+ /**
712
+ * Press back button (Android) - alias for back()
713
+ */
714
+ async pressBack() {
715
+ await this.back();
716
+ }
717
+ /**
718
+ * Press home button - alias for home()
719
+ */
720
+ async pressHome() {
721
+ await this.home();
722
+ }
723
+ /**
724
+ * Shake device
725
+ */
726
+ async shake() {
727
+ this.ensureInitialized("shake");
728
+ this.logger.debug("Shaking device");
729
+ await this.driver.execute("mobile: shake", {});
730
+ }
731
+ /**
732
+ * Get battery info
733
+ */
734
+ async getBatteryInfo() {
735
+ this.ensureInitialized("getBatteryInfo");
736
+ const info = (await this.driver.execute("mobile: batteryInfo", {}));
737
+ return info;
738
+ }
739
+ /**
740
+ * Push file to device
741
+ */
742
+ async pushFile(remotePath, data) {
743
+ this.ensureInitialized("pushFile");
744
+ this.logger.debug(`Pushing file to: ${remotePath}`);
745
+ await this.driver.pushFile(remotePath, data);
746
+ }
747
+ /**
748
+ * Pull file from device
749
+ */
750
+ async pullFile(remotePath) {
751
+ this.ensureInitialized("pullFile");
752
+ this.logger.debug(`Pulling file from: ${remotePath}`);
753
+ return await this.driver.pullFile(remotePath);
754
+ }
755
+ // ============ Screenshot ============
756
+ /**
757
+ * Take a screenshot
758
+ */
759
+ async takeScreenshot(options = {}) {
760
+ this.ensureInitialized("takeScreenshot");
761
+ const base64 = await this.driver.takeScreenshot();
762
+ const buffer = Buffer.from(base64, "base64");
763
+ // Save to file if path specified
764
+ if (options.path) {
765
+ await this.saveScreenshot(buffer, options);
766
+ }
767
+ return buffer;
768
+ }
769
+ /**
770
+ * Execute script
771
+ */
772
+ async executeScript(script, ...args) {
773
+ this.ensureInitialized("executeScript");
774
+ return (await this.driver.execute(script, ...args));
775
+ }
776
+ // ============ Cleanup ============
777
+ /**
778
+ * Quit the driver and cleanup
779
+ */
780
+ async quit() {
781
+ if (!this.initialized) {
782
+ return;
783
+ }
784
+ // Execute before quit hooks
785
+ await this.hooks.execute(hook_manager_1.HookEvent.BEFORE_DRIVER_QUIT, {
786
+ platform: this.platform,
787
+ driverStatus: this.getStatus(),
788
+ });
789
+ try {
790
+ if (this.driver) {
791
+ await this.driver.deleteSession();
792
+ this.driver = null;
793
+ }
794
+ await super.quit();
795
+ // Execute after quit hooks
796
+ await this.hooks.execute(hook_manager_1.HookEvent.AFTER_DRIVER_QUIT, {
797
+ platform: this.platform,
798
+ });
799
+ }
800
+ catch (error) {
801
+ this.logger.error("Error during MobileDriver quit", {
802
+ error: error.message,
803
+ });
804
+ }
805
+ }
806
+ }
807
+ exports.MobileDriver = MobileDriver;
808
+ //# sourceMappingURL=mobile.driver.js.map