@wdio/appium-service 9.23.2 → 9.24.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 (75) hide show
  1. package/README.md +639 -0
  2. package/build/index.d.ts +2 -1
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +2509 -53
  5. package/build/launcher.d.ts +1 -1
  6. package/build/launcher.d.ts.map +1 -1
  7. package/build/mobileSelectorPerformanceOptimizer/aggregator.d.ts +16 -0
  8. package/build/mobileSelectorPerformanceOptimizer/aggregator.d.ts.map +1 -0
  9. package/build/mobileSelectorPerformanceOptimizer/markdown-formatter.d.ts +11 -0
  10. package/build/mobileSelectorPerformanceOptimizer/markdown-formatter.d.ts.map +1 -0
  11. package/build/mobileSelectorPerformanceOptimizer/mspo-reporter.d.ts +45 -0
  12. package/build/mobileSelectorPerformanceOptimizer/mspo-reporter.d.ts.map +1 -0
  13. package/build/mobileSelectorPerformanceOptimizer/mspo-service.d.ts +27 -0
  14. package/build/mobileSelectorPerformanceOptimizer/mspo-service.d.ts.map +1 -0
  15. package/build/mobileSelectorPerformanceOptimizer/mspo-store.d.ts +50 -0
  16. package/build/mobileSelectorPerformanceOptimizer/mspo-store.d.ts.map +1 -0
  17. package/build/mobileSelectorPerformanceOptimizer/optimizer.d.ts +10 -0
  18. package/build/mobileSelectorPerformanceOptimizer/optimizer.d.ts.map +1 -0
  19. package/build/mobileSelectorPerformanceOptimizer/overwrite.d.ts +6 -0
  20. package/build/mobileSelectorPerformanceOptimizer/overwrite.d.ts.map +1 -0
  21. package/build/mobileSelectorPerformanceOptimizer/reporting-types.d.ts +48 -0
  22. package/build/mobileSelectorPerformanceOptimizer/reporting-types.d.ts.map +1 -0
  23. package/build/mobileSelectorPerformanceOptimizer/types.d.ts +53 -0
  24. package/build/mobileSelectorPerformanceOptimizer/types.d.ts.map +1 -0
  25. package/build/mobileSelectorPerformanceOptimizer/utils/browser-utils.d.ts +6 -0
  26. package/build/mobileSelectorPerformanceOptimizer/utils/browser-utils.d.ts.map +1 -0
  27. package/build/mobileSelectorPerformanceOptimizer/utils/command-timing.d.ts +10 -0
  28. package/build/mobileSelectorPerformanceOptimizer/utils/command-timing.d.ts.map +1 -0
  29. package/build/mobileSelectorPerformanceOptimizer/utils/constants.d.ts +14 -0
  30. package/build/mobileSelectorPerformanceOptimizer/utils/constants.d.ts.map +1 -0
  31. package/build/mobileSelectorPerformanceOptimizer/utils/formatting.d.ts +15 -0
  32. package/build/mobileSelectorPerformanceOptimizer/utils/formatting.d.ts.map +1 -0
  33. package/build/mobileSelectorPerformanceOptimizer/utils/index.d.ts +22 -0
  34. package/build/mobileSelectorPerformanceOptimizer/utils/index.d.ts.map +1 -0
  35. package/build/mobileSelectorPerformanceOptimizer/utils/optimization.d.ts +8 -0
  36. package/build/mobileSelectorPerformanceOptimizer/utils/optimization.d.ts.map +1 -0
  37. package/build/mobileSelectorPerformanceOptimizer/utils/performance-data.d.ts +10 -0
  38. package/build/mobileSelectorPerformanceOptimizer/utils/performance-data.d.ts.map +1 -0
  39. package/build/mobileSelectorPerformanceOptimizer/utils/reporter.d.ts +16 -0
  40. package/build/mobileSelectorPerformanceOptimizer/utils/reporter.d.ts.map +1 -0
  41. package/build/mobileSelectorPerformanceOptimizer/utils/selector-location.d.ts +20 -0
  42. package/build/mobileSelectorPerformanceOptimizer/utils/selector-location.d.ts.map +1 -0
  43. package/build/mobileSelectorPerformanceOptimizer/utils/selector-testing.d.ts +10 -0
  44. package/build/mobileSelectorPerformanceOptimizer/utils/selector-testing.d.ts.map +1 -0
  45. package/build/mobileSelectorPerformanceOptimizer/utils/selector-utils.d.ts +37 -0
  46. package/build/mobileSelectorPerformanceOptimizer/utils/selector-utils.d.ts.map +1 -0
  47. package/build/mobileSelectorPerformanceOptimizer/utils/test-context.d.ts +24 -0
  48. package/build/mobileSelectorPerformanceOptimizer/utils/test-context.d.ts.map +1 -0
  49. package/build/mobileSelectorPerformanceOptimizer/utils/timing.d.ts +7 -0
  50. package/build/mobileSelectorPerformanceOptimizer/utils/timing.d.ts.map +1 -0
  51. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-class-chain.d.ts +10 -0
  52. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-class-chain.d.ts.map +1 -0
  53. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-conditions.d.ts +26 -0
  54. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-conditions.d.ts.map +1 -0
  55. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-constants.d.ts +64 -0
  56. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-constants.d.ts.map +1 -0
  57. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-converter.d.ts +14 -0
  58. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-converter.d.ts.map +1 -0
  59. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-detection.d.ts +30 -0
  60. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-detection.d.ts.map +1 -0
  61. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-page-source-executor.d.ts +30 -0
  62. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-page-source-executor.d.ts.map +1 -0
  63. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-page-source.d.ts +20 -0
  64. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-page-source.d.ts.map +1 -0
  65. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-parser.d.ts +9 -0
  66. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-parser.d.ts.map +1 -0
  67. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-predicate.d.ts +18 -0
  68. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-predicate.d.ts.map +1 -0
  69. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-selector-builder.d.ts +11 -0
  70. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-selector-builder.d.ts.map +1 -0
  71. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-types.d.ts +68 -0
  72. package/build/mobileSelectorPerformanceOptimizer/utils/xpath-types.d.ts.map +1 -0
  73. package/build/types.d.ts +46 -0
  74. package/build/types.d.ts.map +1 -1
  75. package/package.json +9 -6
package/README.md CHANGED
@@ -3,6 +3,10 @@ WebdriverIO Appium Service
3
3
 
4
4
  Handling the Appium server is out of the scope of the actual WebdriverIO project. This service helps you to run the Appium server seamlessly when running tests with the [WDIO testrunner](https://webdriver.io/docs/clioptions). It starts the [Appium Server](https://appium.io/docs/en/latest/quickstart/install/#starting-appium) in a child process.
5
5
 
6
+ This package also includes a CLI command (`npx start-appium-inspector`) to quickly start the Appium server and open the Appium Inspector in your browser. See the [CLI Command](#cli-command) section for usage instructions.
7
+
8
+ Additionally, this package also includes a **BETA feature** - the **Native Mobile Selector Performance Optimizer** - which helps identify and optimize slow XPath selectors in your mobile tests. It tracks selector performance during test execution, suggests optimized alternatives, and validates them by testing optimized selectors during the run. At the end, you'll receive a comprehensive report showing which selectors should be replaced in your code for better performance. See the [Native Mobile Selector Performance Optimizer](#native-mobile-selector-performance-optimizer) section for details.
9
+
6
10
  ## Installation
7
11
 
8
12
  The easiest way is to keep `@wdio/appium-service` as a devDependency in your `package.json`, via:
@@ -187,6 +191,641 @@ The CLI automatically opens the Appium Inspector web application at `http://loca
187
191
  - The Appium Inspector requires CORS to be enabled on the Appium server. The CLI automatically adds the `--allow-cors` flag to ensure compatibility.
188
192
  - The CLI uses the `--use-plugins=inspector` flag to enable the Appium Inspector plugin. Before running the command, make sure you have installed the Appium Inspector plugin (see Prerequisites above).
189
193
 
194
+ ## Native Mobile Selector Performance Optimizer
195
+
196
+ **⚠️ BETA Feature** - This feature is currently in beta. All feedback is welcome!
197
+
198
+ The Native Mobile Selector Performance Optimizer helps identify and optimize slow XPath selectors in your mobile tests. During test execution, it:
199
+ - Tracks all XPath selector performance and measures execution times
200
+ - Analyzes XPath selectors and suggests optimized alternatives (iOS class chain, accessibility ID, etc.)
201
+ - **Validates** optimized selectors during the test run to ensure they work correctly
202
+ - Generates a comprehensive report at the end showing which selectors need to be replaced in your code
203
+
204
+ **Important:** This feature **does not replace selectors in your code automatically**. Instead, it provides a report with recommendations. You need to manually update your code based on the report findings. The feature only replaces selectors during test execution for validation purposes.
205
+
206
+ **⚠️ Performance Impact:** Enabling this feature adds significant overhead to your test execution time as it requires fetching and parsing the page source for each selector analysis. **Do not enable this feature (constantly) in your CI/CD pipeline** as it will slow down your tests. Recommended workflow:
207
+ 1. Enable the feature and run your tests
208
+ 2. Review the generated performance report
209
+ 3. Update your code with the optimized selectors from the report
210
+ 4. Create a PR with the optimizations
211
+ 5. Merge the PR
212
+ 6. Disable the feature and run tests normally
213
+
214
+ **Note:** Currently optimized for iOS only. Android support is coming in a future release.
215
+
216
+ ### Configuration
217
+
218
+ To enable the Native Mobile Selector Performance Optimizer, add `trackSelectorPerformance` to your Appium service configuration:
219
+
220
+ ```js
221
+ // wdio.conf.js
222
+ export const config = {
223
+ // ...
224
+ services: [
225
+ ['appium', {
226
+ trackSelectorPerformance: {
227
+ pageObjectPaths: ['./tests/pageobjects']
228
+ }
229
+ }]
230
+ ],
231
+ // ...
232
+ };
233
+ ```
234
+
235
+ ### Options
236
+
237
+ #### enableCliReport
238
+
239
+ Enable or disable the CLI report output to the terminal. When enabled, a formatted performance report is printed to the terminal after test execution.
240
+
241
+ **Note:** The JSON report is always generated when `trackSelectorPerformance` is configured. This option only controls whether the report is also printed to the terminal.
242
+
243
+ Type: `boolean`
244
+
245
+ Default: `false`
246
+
247
+ Example:
248
+ ```js
249
+ export const config = {
250
+ // ...
251
+ services: [
252
+ ['appium', {
253
+ trackSelectorPerformance: {
254
+ pageObjectPaths: ['./tests/pageobjects'],
255
+ enableCliReport: true // Enable terminal output
256
+ }
257
+ }]
258
+ ],
259
+ // ...
260
+ }
261
+ ```
262
+
263
+ #### enableMarkdownReport
264
+
265
+ Enable markdown report file generation. When enabled, a markdown file with the same content as the CLI report is written to the logs folder (the same directory as the JSON report).
266
+
267
+ **Note:** The JSON report is always generated when `trackSelectorPerformance` is configured. This option only controls whether the report is also printed to the terminal.
268
+
269
+ Type: `boolean`
270
+
271
+ Default: `false`
272
+
273
+ Example:
274
+ ```js
275
+ export const config = {
276
+ // ...
277
+ services: [
278
+ ['appium', {
279
+ trackSelectorPerformance: {
280
+ pageObjectPaths: ['./tests/pageobjects'],
281
+ enableMarkdownReport: true // Generate a markdown report file
282
+ }
283
+ }]
284
+ ],
285
+ // ...
286
+ }
287
+ ```
288
+
289
+ #### reportPath
290
+
291
+ Path where the performance report files (JSON, and optionally markdown) should be saved. If not provided, falls back to `config.outputDir`, then `appium` service `logPath`. If none are set, an error will be thrown.
292
+
293
+ Type: `string`
294
+
295
+ Example:
296
+ ```js
297
+ export const config = {
298
+ // ...
299
+ services: [
300
+ ['appium', {
301
+ trackSelectorPerformance: {
302
+ pageObjectPaths: ['./tests/pageobjects'],
303
+ reportPath: './reports/selector-performance'
304
+ }
305
+ }]
306
+ ],
307
+ // ...
308
+ }
309
+ ```
310
+
311
+ #### maxLineLength
312
+
313
+ Maximum line length for terminal and markdown report output. Lines longer than this will be wrapped at word boundaries.
314
+
315
+ Type: `number`
316
+
317
+ Default: `100`
318
+
319
+ Example:
320
+ ```js
321
+ export const config = {
322
+ // ...
323
+ services: [
324
+ ['appium', {
325
+ trackSelectorPerformance: {
326
+ pageObjectPaths: ['./tests/pageobjects'],
327
+ maxLineLength: 120
328
+ }
329
+ }]
330
+ ],
331
+ // ...
332
+ }
333
+ ```
334
+
335
+ #### pageObjectPaths
336
+
337
+ Paths to directories containing page objects or helper files where selectors may be defined. The service will search these directories to find selector locations and show file paths (e.g., "📍 Found at: TabBar.ts:3") in the report.
338
+
339
+ **This option is required.** The service will throw an error if it is not provided.
340
+
341
+ Type: `string[]`
342
+
343
+ Example:
344
+ ```js
345
+ export const config = {
346
+ // ...
347
+ services: [
348
+ ['appium', {
349
+ trackSelectorPerformance: {
350
+ // Single directory
351
+ pageObjectPaths: ['./tests/pageobjects']
352
+ // Or multiple directories
353
+ // pageObjectPaths: ['./tests/pageobjects', './tests/pages', './tests/helpers']
354
+ }
355
+ }]
356
+ ],
357
+ // ...
358
+ }
359
+ ```
360
+
361
+ When `pageObjectPaths` is configured, the report will show file locations with line numbers:
362
+
363
+ ```
364
+ 📍 Found at: tests/screenobjects/components/TabBar.ts:3
365
+ ```
366
+
367
+ #### Complete Example
368
+
369
+ ```js
370
+ export const config = {
371
+ // ...
372
+ services: [
373
+ ['appium', {
374
+ trackSelectorPerformance: {
375
+ usePageSource: true,
376
+ enableCliReport: true,
377
+ enableMarkdownReport: true,
378
+ reportPath: './reports/selector-performance',
379
+ maxLineLength: 100,
380
+ pageObjectPaths: ['./tests/pageobjects', './tests/helpers']
381
+ }
382
+ }]
383
+ ],
384
+ // ...
385
+ }
386
+ ```
387
+
388
+ ### Using with Cloud Services (BrowserStack, Sauce Labs, etc.)
389
+
390
+ The Mobile Selector Performance Optimizer works independently of the local Appium server. This means you can use it with cloud-based testing services like BrowserStack, Sauce Labs, or any other Appium cloud provider.
391
+
392
+ When cloud capabilities are detected, the `@wdio/appium-service` automatically skips starting a local Appium server (since the cloud provider manages Appium for you), but the MSPO feature continues to work normally. It hooks into WebdriverIO's command lifecycle to track selector performance, regardless of where the Appium server is running.
393
+
394
+ **Example configuration with BrowserStack:**
395
+
396
+ ```js
397
+ // wdio.conf.js
398
+ export const config = {
399
+ // ...
400
+ services: [
401
+ ['browserstack', {
402
+ // BrowserStack service options
403
+ }],
404
+ ['appium', {
405
+ // No need to configure Appium server options - it won't start locally
406
+ // Just configure the MSPO feature:
407
+ trackSelectorPerformance: {
408
+ pageObjectPaths: ['./tests/pageobjects'],
409
+ enableCliReport: true,
410
+ enableMarkdownReport: true,
411
+ reportPath: './reports/selector-performance'
412
+ }
413
+ }]
414
+ ],
415
+ // ...
416
+ };
417
+ ```
418
+
419
+ **Example configuration with Sauce Labs:**
420
+
421
+ ```js
422
+ // wdio.conf.js
423
+ export const config = {
424
+ // ...
425
+ services: [
426
+ ['sauce', {
427
+ // Sauce Labs service options
428
+ }],
429
+ ['appium', {
430
+ trackSelectorPerformance: {
431
+ pageObjectPaths: ['./tests/pageobjects'],
432
+ enableCliReport: true,
433
+ enableMarkdownReport: true,
434
+ reportPath: './reports/selector-performance'
435
+ }
436
+ }]
437
+ ],
438
+ // ...
439
+ };
440
+ ```
441
+
442
+ **What happens:**
443
+ - The Appium launcher detects cloud capabilities and logs: `Could not identify any capability that indicates a local Appium session, skipping Appium launch`
444
+ - MSPO tracks all selector performance during test execution on the cloud device
445
+ - Each worker writes its performance data locally
446
+ - After all tests complete, the aggregator combines data from all workers and generates the final report
447
+
448
+ **Expected output:** The same comprehensive performance report (JSON, CLI, and/or Markdown) is generated locally, containing all selector performance data collected from your cloud-based iOS test runs. See the [Report Output](#report-output) section for sample output.
449
+
450
+ ### Logging
451
+
452
+ The Mobile Selector Performance Optimizer logs via `@wdio/logger` with the namespace `@wdio/appium-service:selector-optimizer`. This means that all output respects WebdriverIO `logLevel` and per-logger overrides.
453
+
454
+ **Log Level Hierarchy** (from most verbose to silent):
455
+ - `trace` → Most verbose, includes all log messages
456
+ - `debug` → Includes debug, info, warn, and error messages
457
+ - `info` → Includes info, warn, and error messages (default)
458
+ - `warn` → Includes only warn and error messages
459
+ - `error` → Includes only error messages
460
+ - `silent` → No log output
461
+
462
+ When you set a log level, all levels at or above that level will be shown. For example, setting `logLevel: 'info'` will show `info`, `warn`, and `error` messages, but not `debug` or `trace` messages.
463
+
464
+ To silence the optimizer logs entirely, set:
465
+
466
+ ```js
467
+ export const config = {
468
+ // ...
469
+ logLevel: 'silent', // silences all @wdio/logger output, including the optimizer
470
+ // ...
471
+ }
472
+ ```
473
+
474
+ ### How It Works
475
+
476
+ #### During Test Execution
477
+
478
+ 1. **Tracking**: The service tracks all element-finding commands (`$`, `$$`, `custom$`, `custom$$`) and measures their execution time.
479
+
480
+ 2. **Analysis**: When XPath selectors are detected, the service analyzes them and suggests optimized alternatives (iOS class chain, accessibility ID, etc.).
481
+
482
+ 3. **Validation**: The service automatically tests optimized selectors during the test run to:
483
+ - Verify the optimized selector works correctly
484
+ - Measure actual performance improvements
485
+ - Ensure the suggestion is valid before you update your code
486
+
487
+ 4. **Data Collection**: All performance data is collected along with test context (test file, suite name, test name) for accurate reporting.
488
+
489
+ #### After Test Execution
490
+
491
+ 5. **Report Generation**: At the end of the test run, a comprehensive performance report is generated showing:
492
+ - Top optimizations with the biggest performance gains
493
+ - Quick wins (shared selectors with high impact)
494
+ - All optimizations grouped by test file
495
+ - Performance metrics and improvement percentages
496
+ - Exact selector replacements needed in your code
497
+
498
+ #### Recommended Workflow
499
+
500
+ **Step 1: Initial Analysis Run**
501
+ ```js
502
+ // Enable the feature in your config
503
+ services: [
504
+ ['appium', {
505
+ trackSelectorPerformance: {
506
+ pageObjectPaths: ['./tests/pageobjects'],
507
+ enableCliReport: true, // Enable CLI report output to terminal
508
+ enableMarkdownReport: true // Enable markdown report file generation
509
+ }
510
+ }]
511
+ ]
512
+ ```
513
+
514
+ Run your tests and review the generated report.
515
+
516
+ **Step 2: Review the Report**
517
+ - Check the "Top 10 Most Impactful Optimizations" section for the biggest wins
518
+ - Review "Quick Wins" for shared selectors used across multiple tests
519
+ - Look at "All Actions Required" grouped by test file to see what needs updating
520
+
521
+ **Step 3: Update Your Code**
522
+ - Manually replace XPath selectors in your code with the optimized alternatives from the report
523
+ - Example: Replace `//XCUIElementTypeButton[@name="Submit"]` with `-ios class chain:**/XCUIElementTypeButton[\`name == "Submit"\`]`
524
+
525
+ **Step 4: Create Pull Request**
526
+ - Commit your optimized selectors
527
+ - Create a PR with a clear description of the optimizations
528
+
529
+ **Step 5: Merge and Disable**
530
+ - After merging the PR, disable the feature:
531
+ ```js
532
+ services: [
533
+ ['appium', {
534
+ // trackSelectorPerformance removed to disable the feature
535
+ }]
536
+ ]
537
+ ```
538
+ - Your tests will now run faster without the overhead of the optimizer
539
+
540
+ **⚠️ Important:** Do not keep this feature enabled constantly in your CI/CD pipeline. Use it periodically (e.g., weekly/monthly) to identify new optimization opportunities, then disable it for normal test runs.
541
+
542
+ ### Report Output
543
+
544
+ **Note:** The performance report is only generated when `enableReporter` is `true` (which is the default). When `enableReporter` is `false`, no report is generated and no JSON file is created.
545
+
546
+ When enabled, the service generates a detailed performance report that includes:
547
+ - **Top 10 Most Impactful Optimizations**: Selectors with the biggest performance improvements, showing original selector, optimized selector, and improvement metrics
548
+ - **Quick Wins**: Shared selectors used across multiple tests with high impact - optimize once, benefit everywhere
549
+ - **All Actions Required**: Complete list of optimizations grouped by test file, making it easy to update code systematically
550
+ - **Performance Metrics**: Duration improvements in milliseconds and percentages for each optimization
551
+
552
+ The report is saved as a JSON file in the specified `reportPath` (or default location) and also displayed in the terminal at the end of the test run.
553
+
554
+ #### Sample Report Output
555
+
556
+ <details>
557
+ <summary>Click to expand sample terminal report output</summary>
558
+
559
+ ```
560
+ ═══════════════════════════════════════════════════════════════════════════════
561
+ 📊 Mobile Selector Performance Optimizer Report
562
+ ═══════════════════════════════════════════════════════════════════════════════
563
+
564
+ Device: iPhone 16 Pro
565
+ Run Time: 07:46:00 → 07:53:15 (7m 14s)
566
+ Analyzed: 67 unique selectors (50 optimizable, 17 not recommended)
567
+ Total Potential Savings: 10.98s per test run (2.5% of total run time)
568
+ Average Improvement per Selector: 30.3% faster
569
+
570
+ 📈 Summary
571
+ ───────────────────────────────────────────────────────────────────────────────
572
+ 🔴 High (>50% gain): 2 → Fix immediately
573
+ 🟠 Medium (20-50% gain): 47 → Recommended
574
+ 🟡 Low (10-20% gain): 1 → Minor optimization
575
+ ⚠️ Slower in Testing: 17 → See warnings below
576
+
577
+ 🎯 File-Based Fixes
578
+ ───────────────────────────────────────────────────────────────────────────────
579
+ Update these specific lines for immediate impact:
580
+
581
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/NativeAlert.ts
582
+ L8: $('//XCUIElementTypeAlert') → $("-ios predicate string:type == 'XCUIElementTyp...")
583
+ ⚡ 410.0ms/use × 14 uses = 5.74s total
584
+ └─ File total: 5.74s saved (1 selector)
585
+
586
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts
587
+ L5: $('//*[@name="Drag-drop-screen"]') → $("~Drag-drop-screen")
588
+ ⚡ 61.0ms/use × 3 uses = 183.1ms total
589
+ L8: $('//*[@name="drag-l1"]') → $("~drag-l1") [73.6ms]
590
+ L9: $('//*[@name="drag-c1"]') → $("~drag-c1") [70.1ms]
591
+ L10: $('//*[@name="drag-r1"]') → $("~drag-r1") [73.0ms]
592
+ L11: $('//*[@name="drag-l2"]') → $("~drag-l2") [71.7ms]
593
+ L12: $('//*[@name="drag-c2"]') → $("~drag-c2") [53.9ms]
594
+ L13: $('//*[@name="drag-r2"]') → $("~drag-r2") [51.3ms]
595
+ L14: $('//*[@name="drag-l3"]') → $("~drag-l3") [59.3ms]
596
+ L15: $('//*[@name="drag-c3"]') → $("~drag-c3") [58.1ms]
597
+ L16: $('//*[@name="drag-r3"]') → $("~drag-r3") [50.1ms]
598
+ L26: $('//*[@name="renew"]') → $("~renew") [36.3ms]
599
+ L27: $('//*[@name="button-Retry"]') → $("~button-Retry")
600
+ ⚡ 347.1ms/use × 2 uses = 694.3ms total
601
+ └─ File total: 1.47s saved (12 selectors)
602
+
603
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/FormsScreen.ts
604
+ L13: $('//*[@name="text-input"]') → $("~text-input") [25.4ms]
605
+ L14: $('//*[@name="input-text-result"]') → $("~input-text-result")
606
+ ⚡ 32.7ms/use × 2 uses = 65.3ms total
607
+ L15: $('//*[@name="switch"]') → $("~switch")
608
+ ⚡ 36.5ms/use × 5 uses = 182.4ms total
609
+ L18: $('//*[@name="dropdown-chevron"]') → $("~dropdown-chevron")
610
+ ⚡ 27.4ms/use × 3 uses = 82.3ms total
611
+ L19: $('//*[@name="button-Active"]') → $("~button-Active")
612
+ ⚡ 57.3ms/use × 4 uses = 229.3ms total
613
+ L20: $('//*[@name="button-Inactive"]') → $("~button-Inactive")
614
+ ⚡ 47.7ms/use × 2 uses = 95.4ms total
615
+ L72: $('//*[@name="Dropdown"]//XCUIElementTypeTextFie...') → $("~text_input")
616
+ ⚡ 31.8ms/use × 3 uses = 95.4ms total
617
+ └─ File total: 775.4ms saved (7 selectors)
618
+
619
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/LoginScreen.ts
620
+ L13: $('//*[@name="button-login-container"]') → $("~button-login-container")
621
+ ⚡ 24.1ms/use × 3 uses = 72.4ms total
622
+ L14: $('//*[@name="button-sign-up-container"]') → $("~button-sign-up-container")
623
+ [23.6ms]
624
+ L15: $('//*[@name="button-LOGIN"]') → $("~button-LOGIN")
625
+ ⚡ 56.6ms/use × 2 uses = 113.3ms total
626
+ L16: $('//*[@name="button-SIGN UP"]') → $("~button-SIGN UP")
627
+ ⚡ 53.3ms/use × 2 uses = 106.6ms total
628
+ L17: $('//*[@name="input-email"]') → $("~input-email")
629
+ ⚡ 55.4ms/use × 2 uses = 110.8ms total
630
+ L18: $('//*[@name="input-password"]') → $("~input-password")
631
+ ⚡ 35.6ms/use × 2 uses = 71.3ms total
632
+ L19: $('//*[@name="input-repeat-password"]') → $("~input-repeat-password") [41.7ms]
633
+ L20: $('//*[@name="button-biometric"]') → $("~button-biometric")
634
+ ⚡ 29.2ms/use × 4 uses = 116.7ms total
635
+ └─ File total: 656.3ms saved (8 selectors)
636
+
637
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/Picker.ts
638
+ L3: $('//XCUIElementTypePickerWheel') → $("-ios predicate string:type ==
639
+ 'XCUIElementTyp...")
640
+ ⚡ 46.2ms/use × 6 uses = 277.4ms total
641
+ L4: $('//*[@name="done_button"]') → $("~done_button")
642
+ ⚡ 48.2ms/use × 3 uses = 144.7ms total
643
+ └─ File total: 422.1ms saved (2 selectors)
644
+
645
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/TabBar.ts
646
+ L15: $('//*[@name="Forms"]') → $("~Forms")
647
+ ⚡ 29.5ms/use × 6 uses = 177.0ms total
648
+ L19: $('//*[@name="Swipe"]') → $("~Swipe")
649
+ ⚡ 35.1ms/use × 3 uses = 105.3ms total
650
+ L23: $('//*[@name="Drag"]') → $("~Drag")
651
+ ⚡ 33.2ms/use × 2 uses = 66.4ms total
652
+ └─ File total: 348.8ms saved (3 selectors)
653
+
654
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/SwipeScreen.ts
655
+ L10: $('//*[@name="WebdriverIO logo"]') → $("~WebdriverIO logo")
656
+ ⚡ 86.9ms/use × 2 uses = 173.9ms total
657
+ └─ File total: 173.9ms saved (1 selector)
658
+
659
+ 📁 tests/specs/app.biometric.login.spec.ts
660
+ L64: $('//XCUIElementTypeStaticText[@name="LOGIN"]/an...') → $("~button-LOGIN") [24.0ms]
661
+ L66: $('//XCUIElementTypeStaticText[@name="LOGIN"]/pa...') → $("~button-LOGIN") [22.0ms]
662
+ L67: $('//XCUIElementTypeStaticText[@name="LOGIN"]/.....') → $("~button-LOGIN") [21.6ms]
663
+ L68: $('//XCUIElementTypeOther[@name="button-LOGIN"]/...') → $("~button-biometric")
664
+ [21.2ms]
665
+ L69: $('//XCUIElementTypeOther[@name="button-biometri...') → $("~button-LOGIN") [23.6ms]
666
+ └─ File total: 112.4ms saved (5 selectors)
667
+
668
+ 📁 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/HomeScreen.ts
669
+ L5: $('//*[@name="Home-screen"]') → $("~Home-screen")
670
+ ⚡ 27.4ms/use × 2 uses = 54.8ms total
671
+ └─ File total: 54.8ms saved (1 selector)
672
+
673
+ 🔍 Workspace-Wide Optimizations
674
+ ───────────────────────────────────────────────────────────────────────────────
675
+ Source file unknown. Search your IDE (Cmd+Shift+F) for these selectors:
676
+
677
+ $('//*[@name="Carousel"]') → $("~Carousel")
678
+ ⚡ 57.0ms/use × 10 uses = 570.5ms total
679
+ $('//*[@name="OK"]') → $("~OK")
680
+ ⚡ 48.5ms/use × 4 uses = 193.9ms total
681
+ $('//*[@name="__CAROUSEL_ITEM_0__"]') → $("~__CAROUSEL_ITEM_0__")
682
+ ⚡ 51.1ms/use × 2 uses = 102.2ms total
683
+ $('//*[@name="Cancel"]') → $("~Cancel")
684
+ ⚡ 38.9ms/use × 2 uses = 77.7ms total
685
+ $('//*[@name="__CAROUSEL_ITEM_5__"]') → $("~__CAROUSEL_ITEM_5__")
686
+ ⚡ 61.3ms
687
+ $('//*[@name="__CAROUSEL_ITEM_4__"]') → $("~__CAROUSEL_ITEM_4__")
688
+ ⚡ 54.9ms
689
+ $('//*[@name="__CAROUSEL_ITEM_3__"]') → $("~__CAROUSEL_ITEM_3__")
690
+ ⚡ 51.8ms
691
+ $('//*[@name="__CAROUSEL_ITEM_2__"]') → $("~__CAROUSEL_ITEM_2__")
692
+ ⚡ 41.4ms
693
+ $('//*[@name="__CAROUSEL_ITEM_1__"]') → $("~__CAROUSEL_ITEM_1__")
694
+ ⚡ 37.2ms
695
+ $('//*[@name="Ask me later"]') → $("~Ask me later")
696
+ ⚡ 29.4ms
697
+
698
+ ⚠️ Performance Warnings
699
+ ───────────────────────────────────────────────────────────────────────────────
700
+ Native selectors were SLOWER than XPath for these cases.
701
+ This can happen due to app-specific optimizations, element hierarchy,
702
+ caching effects, or Appium/driver version differences.
703
+ Recommendation: Keep using XPath for these selectors.
704
+
705
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/TabBar.ts:3
706
+ XPath: $('//*[@name="Home"]') → 841ms
707
+ Native: $('~Home') → 1052ms
708
+ ❌ Native was 212ms slower (25%)
709
+
710
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/TabBar.ts:11
711
+ XPath: $('//*[@name="Login"]') → 902ms
712
+ Native: $('~Login') → 1068ms
713
+ ❌ Native was 167ms slower (18%)
714
+
715
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/LoginScreen.ts:4
716
+ XPath: $('//*[@name="Login-screen"]') → 118ms
717
+ Native: $('~Login-screen') → 431ms
718
+ ❌ Native was 313ms slower (265%)
719
+
720
+ 📍 tests/specs/app.biometric.login.spec.ts:65
721
+ XPath: $('//XCUIElementTypeStaticText[@name="LOGIN...') → 96ms
722
+ Native: $('~button-LOGIN') → 106ms
723
+ ❌ Native was 10ms slower (11%)
724
+
725
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/FormsScreen.ts:4
726
+ XPath: $('//*[@name="Forms-screen"]') → 126ms
727
+ Native: $('~Forms-screen') → 507ms
728
+ ❌ Native was 381ms slower (303%)
729
+
730
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/SwipeScreen.ts:2
731
+ XPath: $('//*[@name="Swipe-screen"]') → 158ms
732
+ Native: $('~Swipe-screen') → 1134ms
733
+ ❌ Native was 976ms slower (619%)
734
+
735
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:17
736
+ XPath: $('//*[@name="drop-l1"]') → 124ms
737
+ Native: $('~drop-l1') → 582ms
738
+ ❌ Native was 458ms slower (370%)
739
+
740
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:18
741
+ XPath: $('//*[@name="drop-c1"]') → 122ms
742
+ Native: $('~drop-c1') → 491ms
743
+ ❌ Native was 369ms slower (303%)
744
+
745
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:19
746
+ XPath: $('//*[@name="drop-r1"]') → 122ms
747
+ Native: $('~drop-r1') → 452ms
748
+ ❌ Native was 330ms slower (271%)
749
+
750
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:20
751
+ XPath: $('//*[@name="drop-l2"]') → 114ms
752
+ Native: $('~drop-l2') → 420ms
753
+ ❌ Native was 306ms slower (268%)
754
+
755
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:21
756
+ XPath: $('//*[@name="drop-c2"]') → 100ms
757
+ Native: $('~drop-c2') → 371ms
758
+ ❌ Native was 270ms slower (269%)
759
+
760
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:22
761
+ XPath: $('//*[@name="drop-r2"]') → 93ms
762
+ Native: $('~drop-r2') → 334ms
763
+ ❌ Native was 240ms slower (257%)
764
+
765
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:23
766
+ XPath: $('//*[@name="drop-l3"]') → 92ms
767
+ Native: $('~drop-l3') → 279ms
768
+ ❌ Native was 187ms slower (204%)
769
+
770
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:24
771
+ XPath: $('//*[@name="drop-c3"]') → 87ms
772
+ Native: $('~drop-c3') → 237ms
773
+ ❌ Native was 150ms slower (172%)
774
+
775
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/DragScreen.ts:25
776
+ XPath: $('//*[@name="drop-r3"]') → 80ms
777
+ Native: $('~drop-r3') → 198ms
778
+ ❌ Native was 118ms slower (148%)
779
+
780
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/components/TabBar.ts:7
781
+ XPath: $('//*[@name="Webview"]') → 948ms
782
+ Native: $('~Webview') → 1060ms
783
+ ❌ Native was 111ms slower (12%)
784
+
785
+ 📍 /Users/wimselles/Git/wdio/appium-boilerplate/tests/screenobjects/WebviewScreen.ts:8
786
+ XPath: $('*//XCUIElementTypeWebView') → 102ms
787
+ Native: $('-ios predicate string:type == 'XCUIEleme...') → 153ms
788
+ ❌ Native was 51ms slower (50%)
789
+
790
+ 💡 Why Change?
791
+ ───────────────────────────────────────────────────────────────────────────────
792
+ • Speed: Native selectors bypass expensive XML tree traversal
793
+ • Stability: Less affected by UI hierarchy changes
794
+ • Priority: ~accessibilityId > -ios predicate string > -ios class chain > //xpath
795
+ • Docs: https://webdriver.io/docs/selectors#mobile-selectors
796
+ ═══════════════════════════════════════════════════════════════════════════════
797
+
798
+ ───────────────────────────────────────────────────────────────────────────────
799
+ 📝 Mobile Selector Performance Optimizer - Markdown Report
800
+ ───────────────────────────────────────────────────────────────────────────────
801
+ 📁 Markdown report written to: /Users/wimselles/Git/wdio/appium-boilerplate/logs/mobile-selector-performance-optimizer-report-iphone_16_pro-1769237599894.md
802
+ ───────────────────────────────────────────────────────────────────────────────
803
+ ```
804
+
805
+ </details>
806
+
807
+ #### Understanding Performance Results
808
+
809
+ While native selectors (like accessibility IDs) are generally faster than XPath, you may occasionally see cases in your report where the suggested selector performed slower during testing. This is normal and can happen for a few reasons:
810
+
811
+ - **Timing variations**: Mobile devices aren't perfectly consistent. Background processes, screen animations, or momentary CPU load can affect individual measurements. A selector tested during a busy moment may appear slower than one tested when the device was idle.
812
+
813
+ - **App-specific behavior**: Some apps have unique UI structures where certain selector strategies work better than others. The "typical" performance ranking doesn't apply universally to every element in every app.
814
+
815
+ - **Measurement represents a single snapshot**: Each selector is tested once during your test run. This captures real performance but includes normal variation. A selector showing 10-15% slower results may actually perform the same or better on average.
816
+
817
+ **What to do with "slower" results**: The report flags these cases in the "Performance Warnings" section so you can make informed decisions. For selectors showing significantly slower native performance (50%+), it's reasonable to keep using XPath. For borderline cases, the difference is likely negligible in practice, and native selectors are typically more stable and less brittle than XPath since they don't depend on the exact UI hierarchy, which can change between app versions.
818
+
819
+ The optimizer helps you find clear wins, and most selectors will show genuine improvements. The warnings simply ensure you have complete information rather than blindly replacing every selector.
820
+
821
+ #### Platform Support
822
+
823
+ - ✅ **iOS**: Fully supported and optimized
824
+ - ⚠️ **Android**: Currently disabled (support coming in a future release)
825
+ - ⚠️ **MultiRemote**: Not supported yet (feature is automatically disabled for MultiRemote sessions)
826
+
827
+ When running on Android or with MultiRemote, the service will log a warning message indicating it's disabled and skip optimization.
828
+
190
829
  ----
191
830
 
192
831
  For more information on WebdriverIO see the [homepage](https://webdriver.io).
package/build/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import AppiumLauncher from './launcher.js';
2
- export default class AppiumService {
2
+ import SelectorPerformanceService from './mobileSelectorPerformanceOptimizer/mspo-service.js';
3
+ export default class AppiumService extends SelectorPerformanceService {
3
4
  }
4
5
  export declare const launcher: typeof AppiumLauncher;
5
6
  export * from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,cAAc,MAAM,eAAe,CAAA;AAE1C,MAAM,CAAC,OAAO,OAAO,aAAa;CAAG;AACrC,eAAO,MAAM,QAAQ,uBAAiB,CAAA;AAEtC,cAAc,YAAY,CAAA;AAC1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,WAAW,CAAC;QAClB,UAAU,aAAc,SAAQ,mBAAmB;SAAG;KACzD;CACJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,cAAc,MAAM,eAAe,CAAA;AAC1C,OAAO,0BAA0B,MAAM,sDAAsD,CAAA;AAE7F,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,0BAA0B;CAAG;AACxE,eAAO,MAAM,QAAQ,uBAAiB,CAAA;AAEtC,cAAc,YAAY,CAAA;AAC1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,WAAW,CAAC;QAClB,UAAU,aAAc,SAAQ,mBAAmB;SAAG;KACzD;CACJ"}