doc-detective 4.22.0 → 4.23.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 (53) hide show
  1. package/dist/common/src/schemas/schemas.json +274 -74
  2. package/dist/common/src/types/generated/config_v3.d.ts +1 -1
  3. package/dist/common/src/types/generated/context_v3.d.ts +1 -1
  4. package/dist/common/src/types/generated/report_v3.d.ts +1 -1
  5. package/dist/common/src/types/generated/resolvedTests_v3.d.ts +2 -2
  6. package/dist/common/src/types/generated/spec_v3.d.ts +1 -1
  7. package/dist/common/src/types/generated/test_v3.d.ts +1 -1
  8. package/dist/core/appium.d.ts +2 -1
  9. package/dist/core/appium.d.ts.map +1 -1
  10. package/dist/core/appium.js +47 -13
  11. package/dist/core/appium.js.map +1 -1
  12. package/dist/core/ptyProbeWorker.d.ts +2 -0
  13. package/dist/core/ptyProbeWorker.d.ts.map +1 -0
  14. package/dist/core/ptyProbeWorker.js +61 -0
  15. package/dist/core/ptyProbeWorker.js.map +1 -0
  16. package/dist/core/ptyWatchdog.d.ts +77 -0
  17. package/dist/core/ptyWatchdog.d.ts.map +1 -0
  18. package/dist/core/ptyWatchdog.js +179 -0
  19. package/dist/core/ptyWatchdog.js.map +1 -0
  20. package/dist/core/resolveTests.d.ts.map +1 -1
  21. package/dist/core/resolveTests.js +16 -4
  22. package/dist/core/resolveTests.js.map +1 -1
  23. package/dist/core/tests/appSurface.d.ts +21 -2
  24. package/dist/core/tests/appSurface.d.ts.map +1 -1
  25. package/dist/core/tests/appSurface.js +117 -3
  26. package/dist/core/tests/appSurface.js.map +1 -1
  27. package/dist/core/tests/browserWait.d.ts +1 -1
  28. package/dist/core/tests/browserWait.d.ts.map +1 -1
  29. package/dist/core/tests/browserWait.js +108 -84
  30. package/dist/core/tests/browserWait.js.map +1 -1
  31. package/dist/core/tests/findElement.d.ts.map +1 -1
  32. package/dist/core/tests/findElement.js +16 -5
  33. package/dist/core/tests/findElement.js.map +1 -1
  34. package/dist/core/tests/mobileBrowser.d.ts +46 -0
  35. package/dist/core/tests/mobileBrowser.d.ts.map +1 -0
  36. package/dist/core/tests/mobileBrowser.js +140 -0
  37. package/dist/core/tests/mobileBrowser.js.map +1 -0
  38. package/dist/core/tests/mobilePlatform.d.ts +1 -7
  39. package/dist/core/tests/mobilePlatform.d.ts.map +1 -1
  40. package/dist/core/tests/mobilePlatform.js +5 -27
  41. package/dist/core/tests/mobilePlatform.js.map +1 -1
  42. package/dist/core/tests.d.ts +7 -1
  43. package/dist/core/tests.d.ts.map +1 -1
  44. package/dist/core/tests.js +234 -58
  45. package/dist/core/tests.js.map +1 -1
  46. package/dist/core/utils.d.ts.map +1 -1
  47. package/dist/core/utils.js +26 -1
  48. package/dist/core/utils.js.map +1 -1
  49. package/dist/index.cjs +1039 -409
  50. package/dist/runtime/loader.d.ts.map +1 -1
  51. package/dist/runtime/loader.js +76 -0
  52. package/dist/runtime/loader.js.map +1 -1
  53. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -2549,7 +2549,7 @@ var init_schemas = __esm({
2549
2549
  ]
2550
2550
  },
2551
2551
  browsers: {
2552
- description: "Browsers to run tests on.",
2552
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
2553
2553
  anyOf: [
2554
2554
  {
2555
2555
  type: "string",
@@ -2560,7 +2560,7 @@ var init_schemas = __esm({
2560
2560
  "safari",
2561
2561
  "webkit"
2562
2562
  ],
2563
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2563
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2564
2564
  },
2565
2565
  {
2566
2566
  type: "object",
@@ -2579,7 +2579,7 @@ var init_schemas = __esm({
2579
2579
  "safari",
2580
2580
  "webkit"
2581
2581
  ],
2582
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2582
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2583
2583
  },
2584
2584
  explicit: {
2585
2585
  type: "boolean",
@@ -2639,7 +2639,7 @@ var init_schemas = __esm({
2639
2639
  "safari",
2640
2640
  "webkit"
2641
2641
  ],
2642
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2642
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2643
2643
  },
2644
2644
  {
2645
2645
  type: "object",
@@ -2658,7 +2658,7 @@ var init_schemas = __esm({
2658
2658
  "safari",
2659
2659
  "webkit"
2660
2660
  ],
2661
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2661
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2662
2662
  },
2663
2663
  explicit: {
2664
2664
  type: "boolean",
@@ -2944,7 +2944,7 @@ var init_schemas = __esm({
2944
2944
  "safari",
2945
2945
  "webkit"
2946
2946
  ],
2947
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2947
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2948
2948
  },
2949
2949
  browser: {
2950
2950
  type: "object",
@@ -2963,7 +2963,7 @@ var init_schemas = __esm({
2963
2963
  "safari",
2964
2964
  "webkit"
2965
2965
  ],
2966
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
2966
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
2967
2967
  },
2968
2968
  explicit: {
2969
2969
  type: "boolean",
@@ -3103,6 +3103,26 @@ var init_schemas = __esm({
3103
3103
  }
3104
3104
  }
3105
3105
  ]
3106
+ },
3107
+ {
3108
+ platforms: "android"
3109
+ },
3110
+ {
3111
+ platforms: [
3112
+ "windows",
3113
+ "mac",
3114
+ "android",
3115
+ "ios"
3116
+ ],
3117
+ browsers: "chrome"
3118
+ },
3119
+ {
3120
+ platforms: "ios",
3121
+ browsers: "safari",
3122
+ device: {
3123
+ name: "test-iphone",
3124
+ deviceType: "phone"
3125
+ }
3106
3126
  }
3107
3127
  ]
3108
3128
  }
@@ -87120,7 +87140,7 @@ var init_schemas = __esm({
87120
87140
  ]
87121
87141
  },
87122
87142
  browsers: {
87123
- description: "Browsers to run tests on.",
87143
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
87124
87144
  anyOf: [
87125
87145
  {
87126
87146
  type: "string",
@@ -87131,7 +87151,7 @@ var init_schemas = __esm({
87131
87151
  "safari",
87132
87152
  "webkit"
87133
87153
  ],
87134
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87154
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87135
87155
  },
87136
87156
  {
87137
87157
  type: "object",
@@ -87150,7 +87170,7 @@ var init_schemas = __esm({
87150
87170
  "safari",
87151
87171
  "webkit"
87152
87172
  ],
87153
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87173
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87154
87174
  },
87155
87175
  explicit: {
87156
87176
  type: "boolean",
@@ -87210,7 +87230,7 @@ var init_schemas = __esm({
87210
87230
  "safari",
87211
87231
  "webkit"
87212
87232
  ],
87213
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87233
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87214
87234
  },
87215
87235
  {
87216
87236
  type: "object",
@@ -87229,7 +87249,7 @@ var init_schemas = __esm({
87229
87249
  "safari",
87230
87250
  "webkit"
87231
87251
  ],
87232
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87252
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87233
87253
  },
87234
87254
  explicit: {
87235
87255
  type: "boolean",
@@ -87515,7 +87535,7 @@ var init_schemas = __esm({
87515
87535
  "safari",
87516
87536
  "webkit"
87517
87537
  ],
87518
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87538
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87519
87539
  },
87520
87540
  browser: {
87521
87541
  type: "object",
@@ -87534,7 +87554,7 @@ var init_schemas = __esm({
87534
87554
  "safari",
87535
87555
  "webkit"
87536
87556
  ],
87537
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
87557
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
87538
87558
  },
87539
87559
  explicit: {
87540
87560
  type: "boolean",
@@ -87674,6 +87694,26 @@ var init_schemas = __esm({
87674
87694
  }
87675
87695
  }
87676
87696
  ]
87697
+ },
87698
+ {
87699
+ platforms: "android"
87700
+ },
87701
+ {
87702
+ platforms: [
87703
+ "windows",
87704
+ "mac",
87705
+ "android",
87706
+ "ios"
87707
+ ],
87708
+ browsers: "chrome"
87709
+ },
87710
+ {
87711
+ platforms: "ios",
87712
+ browsers: "safari",
87713
+ device: {
87714
+ name: "test-iphone",
87715
+ deviceType: "phone"
87716
+ }
87677
87717
  }
87678
87718
  ]
87679
87719
  },
@@ -100011,7 +100051,7 @@ var init_schemas = __esm({
100011
100051
  ]
100012
100052
  },
100013
100053
  browsers: {
100014
- description: "Browsers to run tests on.",
100054
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
100015
100055
  anyOf: [
100016
100056
  {
100017
100057
  type: "string",
@@ -100022,7 +100062,7 @@ var init_schemas = __esm({
100022
100062
  "safari",
100023
100063
  "webkit"
100024
100064
  ],
100025
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100065
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100026
100066
  },
100027
100067
  {
100028
100068
  type: "object",
@@ -100041,7 +100081,7 @@ var init_schemas = __esm({
100041
100081
  "safari",
100042
100082
  "webkit"
100043
100083
  ],
100044
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100084
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100045
100085
  },
100046
100086
  explicit: {
100047
100087
  type: "boolean",
@@ -100101,7 +100141,7 @@ var init_schemas = __esm({
100101
100141
  "safari",
100102
100142
  "webkit"
100103
100143
  ],
100104
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100144
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100105
100145
  },
100106
100146
  {
100107
100147
  type: "object",
@@ -100120,7 +100160,7 @@ var init_schemas = __esm({
100120
100160
  "safari",
100121
100161
  "webkit"
100122
100162
  ],
100123
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100163
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100124
100164
  },
100125
100165
  explicit: {
100126
100166
  type: "boolean",
@@ -100406,7 +100446,7 @@ var init_schemas = __esm({
100406
100446
  "safari",
100407
100447
  "webkit"
100408
100448
  ],
100409
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100449
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100410
100450
  },
100411
100451
  browser: {
100412
100452
  type: "object",
@@ -100425,7 +100465,7 @@ var init_schemas = __esm({
100425
100465
  "safari",
100426
100466
  "webkit"
100427
100467
  ],
100428
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
100468
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
100429
100469
  },
100430
100470
  explicit: {
100431
100471
  type: "boolean",
@@ -100565,6 +100605,26 @@ var init_schemas = __esm({
100565
100605
  }
100566
100606
  }
100567
100607
  ]
100608
+ },
100609
+ {
100610
+ platforms: "android"
100611
+ },
100612
+ {
100613
+ platforms: [
100614
+ "windows",
100615
+ "mac",
100616
+ "android",
100617
+ "ios"
100618
+ ],
100619
+ browsers: "chrome"
100620
+ },
100621
+ {
100622
+ platforms: "ios",
100623
+ browsers: "safari",
100624
+ device: {
100625
+ name: "test-iphone",
100626
+ deviceType: "phone"
100627
+ }
100568
100628
  }
100569
100629
  ]
100570
100630
  }
@@ -184644,7 +184704,7 @@ var init_schemas = __esm({
184644
184704
  ]
184645
184705
  },
184646
184706
  browsers: {
184647
- description: "Browsers to run tests on.",
184707
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
184648
184708
  anyOf: [
184649
184709
  {
184650
184710
  type: "string",
@@ -184655,7 +184715,7 @@ var init_schemas = __esm({
184655
184715
  "safari",
184656
184716
  "webkit"
184657
184717
  ],
184658
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
184718
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
184659
184719
  },
184660
184720
  {
184661
184721
  type: "object",
@@ -184674,7 +184734,7 @@ var init_schemas = __esm({
184674
184734
  "safari",
184675
184735
  "webkit"
184676
184736
  ],
184677
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
184737
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
184678
184738
  },
184679
184739
  explicit: {
184680
184740
  type: "boolean",
@@ -184734,7 +184794,7 @@ var init_schemas = __esm({
184734
184794
  "safari",
184735
184795
  "webkit"
184736
184796
  ],
184737
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
184797
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
184738
184798
  },
184739
184799
  {
184740
184800
  type: "object",
@@ -184753,7 +184813,7 @@ var init_schemas = __esm({
184753
184813
  "safari",
184754
184814
  "webkit"
184755
184815
  ],
184756
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
184816
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
184757
184817
  },
184758
184818
  explicit: {
184759
184819
  type: "boolean",
@@ -185039,7 +185099,7 @@ var init_schemas = __esm({
185039
185099
  "safari",
185040
185100
  "webkit"
185041
185101
  ],
185042
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185102
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185043
185103
  },
185044
185104
  browser: {
185045
185105
  type: "object",
@@ -185058,7 +185118,7 @@ var init_schemas = __esm({
185058
185118
  "safari",
185059
185119
  "webkit"
185060
185120
  ],
185061
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185121
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185062
185122
  },
185063
185123
  explicit: {
185064
185124
  type: "boolean",
@@ -185198,6 +185258,26 @@ var init_schemas = __esm({
185198
185258
  }
185199
185259
  }
185200
185260
  ]
185261
+ },
185262
+ {
185263
+ platforms: "android"
185264
+ },
185265
+ {
185266
+ platforms: [
185267
+ "windows",
185268
+ "mac",
185269
+ "android",
185270
+ "ios"
185271
+ ],
185272
+ browsers: "chrome"
185273
+ },
185274
+ {
185275
+ platforms: "ios",
185276
+ browsers: "safari",
185277
+ device: {
185278
+ name: "test-iphone",
185279
+ deviceType: "phone"
185280
+ }
185201
185281
  }
185202
185282
  ]
185203
185283
  }
@@ -185486,7 +185566,7 @@ var init_schemas = __esm({
185486
185566
  ]
185487
185567
  },
185488
185568
  browsers: {
185489
- description: "Browsers to run tests on.",
185569
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
185490
185570
  anyOf: [
185491
185571
  {
185492
185572
  type: "string",
@@ -185497,7 +185577,7 @@ var init_schemas = __esm({
185497
185577
  "safari",
185498
185578
  "webkit"
185499
185579
  ],
185500
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185580
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185501
185581
  },
185502
185582
  {
185503
185583
  type: "object",
@@ -185516,7 +185596,7 @@ var init_schemas = __esm({
185516
185596
  "safari",
185517
185597
  "webkit"
185518
185598
  ],
185519
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185599
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185520
185600
  },
185521
185601
  explicit: {
185522
185602
  type: "boolean",
@@ -185576,7 +185656,7 @@ var init_schemas = __esm({
185576
185656
  "safari",
185577
185657
  "webkit"
185578
185658
  ],
185579
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185659
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185580
185660
  },
185581
185661
  {
185582
185662
  type: "object",
@@ -185595,7 +185675,7 @@ var init_schemas = __esm({
185595
185675
  "safari",
185596
185676
  "webkit"
185597
185677
  ],
185598
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185678
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185599
185679
  },
185600
185680
  explicit: {
185601
185681
  type: "boolean",
@@ -185881,7 +185961,7 @@ var init_schemas = __esm({
185881
185961
  "safari",
185882
185962
  "webkit"
185883
185963
  ],
185884
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185964
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185885
185965
  },
185886
185966
  browser: {
185887
185967
  type: "object",
@@ -185900,7 +185980,7 @@ var init_schemas = __esm({
185900
185980
  "safari",
185901
185981
  "webkit"
185902
185982
  ],
185903
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
185983
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
185904
185984
  },
185905
185985
  explicit: {
185906
185986
  type: "boolean",
@@ -186040,6 +186120,26 @@ var init_schemas = __esm({
186040
186120
  }
186041
186121
  }
186042
186122
  ]
186123
+ },
186124
+ {
186125
+ platforms: "android"
186126
+ },
186127
+ {
186128
+ platforms: [
186129
+ "windows",
186130
+ "mac",
186131
+ "android",
186132
+ "ios"
186133
+ ],
186134
+ browsers: "chrome"
186135
+ },
186136
+ {
186137
+ platforms: "ios",
186138
+ browsers: "safari",
186139
+ device: {
186140
+ name: "test-iphone",
186141
+ deviceType: "phone"
186142
+ }
186043
186143
  }
186044
186144
  ]
186045
186145
  }
@@ -228201,7 +228301,7 @@ var init_schemas = __esm({
228201
228301
  "safari",
228202
228302
  "webkit"
228203
228303
  ],
228204
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
228304
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
228205
228305
  },
228206
228306
  explicit: {
228207
228307
  type: "boolean",
@@ -270545,7 +270645,7 @@ var init_schemas = __esm({
270545
270645
  ]
270546
270646
  },
270547
270647
  browsers: {
270548
- description: "Browsers to run tests on.",
270648
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
270549
270649
  anyOf: [
270550
270650
  {
270551
270651
  type: "string",
@@ -270556,7 +270656,7 @@ var init_schemas = __esm({
270556
270656
  "safari",
270557
270657
  "webkit"
270558
270658
  ],
270559
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
270659
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270560
270660
  },
270561
270661
  {
270562
270662
  type: "object",
@@ -270575,7 +270675,7 @@ var init_schemas = __esm({
270575
270675
  "safari",
270576
270676
  "webkit"
270577
270677
  ],
270578
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
270678
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270579
270679
  },
270580
270680
  explicit: {
270581
270681
  type: "boolean",
@@ -270635,7 +270735,7 @@ var init_schemas = __esm({
270635
270735
  "safari",
270636
270736
  "webkit"
270637
270737
  ],
270638
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
270738
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270639
270739
  },
270640
270740
  {
270641
270741
  type: "object",
@@ -270654,7 +270754,7 @@ var init_schemas = __esm({
270654
270754
  "safari",
270655
270755
  "webkit"
270656
270756
  ],
270657
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
270757
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270658
270758
  },
270659
270759
  explicit: {
270660
270760
  type: "boolean",
@@ -270940,7 +271040,7 @@ var init_schemas = __esm({
270940
271040
  "safari",
270941
271041
  "webkit"
270942
271042
  ],
270943
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271043
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270944
271044
  },
270945
271045
  browser: {
270946
271046
  type: "object",
@@ -270959,7 +271059,7 @@ var init_schemas = __esm({
270959
271059
  "safari",
270960
271060
  "webkit"
270961
271061
  ],
270962
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271062
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
270963
271063
  },
270964
271064
  explicit: {
270965
271065
  type: "boolean",
@@ -271099,6 +271199,26 @@ var init_schemas = __esm({
271099
271199
  }
271100
271200
  }
271101
271201
  ]
271202
+ },
271203
+ {
271204
+ platforms: "android"
271205
+ },
271206
+ {
271207
+ platforms: [
271208
+ "windows",
271209
+ "mac",
271210
+ "android",
271211
+ "ios"
271212
+ ],
271213
+ browsers: "chrome"
271214
+ },
271215
+ {
271216
+ platforms: "ios",
271217
+ browsers: "safari",
271218
+ device: {
271219
+ name: "test-iphone",
271220
+ deviceType: "phone"
271221
+ }
271102
271222
  }
271103
271223
  ]
271104
271224
  }
@@ -271387,7 +271507,7 @@ var init_schemas = __esm({
271387
271507
  ]
271388
271508
  },
271389
271509
  browsers: {
271390
- description: "Browsers to run tests on.",
271510
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
271391
271511
  anyOf: [
271392
271512
  {
271393
271513
  type: "string",
@@ -271398,7 +271518,7 @@ var init_schemas = __esm({
271398
271518
  "safari",
271399
271519
  "webkit"
271400
271520
  ],
271401
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271521
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271402
271522
  },
271403
271523
  {
271404
271524
  type: "object",
@@ -271417,7 +271537,7 @@ var init_schemas = __esm({
271417
271537
  "safari",
271418
271538
  "webkit"
271419
271539
  ],
271420
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271540
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271421
271541
  },
271422
271542
  explicit: {
271423
271543
  type: "boolean",
@@ -271477,7 +271597,7 @@ var init_schemas = __esm({
271477
271597
  "safari",
271478
271598
  "webkit"
271479
271599
  ],
271480
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271600
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271481
271601
  },
271482
271602
  {
271483
271603
  type: "object",
@@ -271496,7 +271616,7 @@ var init_schemas = __esm({
271496
271616
  "safari",
271497
271617
  "webkit"
271498
271618
  ],
271499
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271619
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271500
271620
  },
271501
271621
  explicit: {
271502
271622
  type: "boolean",
@@ -271782,7 +271902,7 @@ var init_schemas = __esm({
271782
271902
  "safari",
271783
271903
  "webkit"
271784
271904
  ],
271785
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271905
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271786
271906
  },
271787
271907
  browser: {
271788
271908
  type: "object",
@@ -271801,7 +271921,7 @@ var init_schemas = __esm({
271801
271921
  "safari",
271802
271922
  "webkit"
271803
271923
  ],
271804
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
271924
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
271805
271925
  },
271806
271926
  explicit: {
271807
271927
  type: "boolean",
@@ -271941,6 +272061,26 @@ var init_schemas = __esm({
271941
272061
  }
271942
272062
  }
271943
272063
  ]
272064
+ },
272065
+ {
272066
+ platforms: "android"
272067
+ },
272068
+ {
272069
+ platforms: [
272070
+ "windows",
272071
+ "mac",
272072
+ "android",
272073
+ "ios"
272074
+ ],
272075
+ browsers: "chrome"
272076
+ },
272077
+ {
272078
+ platforms: "ios",
272079
+ browsers: "safari",
272080
+ device: {
272081
+ name: "test-iphone",
272082
+ deviceType: "phone"
272083
+ }
271944
272084
  }
271945
272085
  ]
271946
272086
  }
@@ -314102,7 +314242,7 @@ var init_schemas = __esm({
314102
314242
  "safari",
314103
314243
  "webkit"
314104
314244
  ],
314105
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
314245
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
314106
314246
  },
314107
314247
  explicit: {
314108
314248
  type: "boolean",
@@ -359659,7 +359799,7 @@ var init_schemas = __esm({
359659
359799
  ]
359660
359800
  },
359661
359801
  browsers: {
359662
- description: "Browsers to run tests on.",
359802
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
359663
359803
  anyOf: [
359664
359804
  {
359665
359805
  type: "string",
@@ -359670,7 +359810,7 @@ var init_schemas = __esm({
359670
359810
  "safari",
359671
359811
  "webkit"
359672
359812
  ],
359673
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
359813
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
359674
359814
  },
359675
359815
  {
359676
359816
  type: "object",
@@ -359689,7 +359829,7 @@ var init_schemas = __esm({
359689
359829
  "safari",
359690
359830
  "webkit"
359691
359831
  ],
359692
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
359832
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
359693
359833
  },
359694
359834
  explicit: {
359695
359835
  type: "boolean",
@@ -359749,7 +359889,7 @@ var init_schemas = __esm({
359749
359889
  "safari",
359750
359890
  "webkit"
359751
359891
  ],
359752
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
359892
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
359753
359893
  },
359754
359894
  {
359755
359895
  type: "object",
@@ -359768,7 +359908,7 @@ var init_schemas = __esm({
359768
359908
  "safari",
359769
359909
  "webkit"
359770
359910
  ],
359771
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
359911
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
359772
359912
  },
359773
359913
  explicit: {
359774
359914
  type: "boolean",
@@ -360054,7 +360194,7 @@ var init_schemas = __esm({
360054
360194
  "safari",
360055
360195
  "webkit"
360056
360196
  ],
360057
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360197
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360058
360198
  },
360059
360199
  browser: {
360060
360200
  type: "object",
@@ -360073,7 +360213,7 @@ var init_schemas = __esm({
360073
360213
  "safari",
360074
360214
  "webkit"
360075
360215
  ],
360076
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360216
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360077
360217
  },
360078
360218
  explicit: {
360079
360219
  type: "boolean",
@@ -360213,6 +360353,26 @@ var init_schemas = __esm({
360213
360353
  }
360214
360354
  }
360215
360355
  ]
360356
+ },
360357
+ {
360358
+ platforms: "android"
360359
+ },
360360
+ {
360361
+ platforms: [
360362
+ "windows",
360363
+ "mac",
360364
+ "android",
360365
+ "ios"
360366
+ ],
360367
+ browsers: "chrome"
360368
+ },
360369
+ {
360370
+ platforms: "ios",
360371
+ browsers: "safari",
360372
+ device: {
360373
+ name: "test-iphone",
360374
+ deviceType: "phone"
360375
+ }
360216
360376
  }
360217
360377
  ]
360218
360378
  }
@@ -360501,7 +360661,7 @@ var init_schemas = __esm({
360501
360661
  ]
360502
360662
  },
360503
360663
  browsers: {
360504
- description: "Browsers to run tests on.",
360664
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
360505
360665
  anyOf: [
360506
360666
  {
360507
360667
  type: "string",
@@ -360512,7 +360672,7 @@ var init_schemas = __esm({
360512
360672
  "safari",
360513
360673
  "webkit"
360514
360674
  ],
360515
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360675
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360516
360676
  },
360517
360677
  {
360518
360678
  type: "object",
@@ -360531,7 +360691,7 @@ var init_schemas = __esm({
360531
360691
  "safari",
360532
360692
  "webkit"
360533
360693
  ],
360534
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360694
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360535
360695
  },
360536
360696
  explicit: {
360537
360697
  type: "boolean",
@@ -360591,7 +360751,7 @@ var init_schemas = __esm({
360591
360751
  "safari",
360592
360752
  "webkit"
360593
360753
  ],
360594
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360754
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360595
360755
  },
360596
360756
  {
360597
360757
  type: "object",
@@ -360610,7 +360770,7 @@ var init_schemas = __esm({
360610
360770
  "safari",
360611
360771
  "webkit"
360612
360772
  ],
360613
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
360773
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360614
360774
  },
360615
360775
  explicit: {
360616
360776
  type: "boolean",
@@ -360896,7 +361056,7 @@ var init_schemas = __esm({
360896
361056
  "safari",
360897
361057
  "webkit"
360898
361058
  ],
360899
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
361059
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360900
361060
  },
360901
361061
  browser: {
360902
361062
  type: "object",
@@ -360915,7 +361075,7 @@ var init_schemas = __esm({
360915
361075
  "safari",
360916
361076
  "webkit"
360917
361077
  ],
360918
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
361078
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
360919
361079
  },
360920
361080
  explicit: {
360921
361081
  type: "boolean",
@@ -361055,6 +361215,26 @@ var init_schemas = __esm({
361055
361215
  }
361056
361216
  }
361057
361217
  ]
361218
+ },
361219
+ {
361220
+ platforms: "android"
361221
+ },
361222
+ {
361223
+ platforms: [
361224
+ "windows",
361225
+ "mac",
361226
+ "android",
361227
+ "ios"
361228
+ ],
361229
+ browsers: "chrome"
361230
+ },
361231
+ {
361232
+ platforms: "ios",
361233
+ browsers: "safari",
361234
+ device: {
361235
+ name: "test-iphone",
361236
+ deviceType: "phone"
361237
+ }
361058
361238
  }
361059
361239
  ]
361060
361240
  }
@@ -403216,7 +403396,7 @@ var init_schemas = __esm({
403216
403396
  "safari",
403217
403397
  "webkit"
403218
403398
  ],
403219
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
403399
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
403220
403400
  },
403221
403401
  explicit: {
403222
403402
  type: "boolean",
@@ -487806,7 +487986,7 @@ var init_schemas = __esm({
487806
487986
  ]
487807
487987
  },
487808
487988
  browsers: {
487809
- description: "Browsers to run tests on.",
487989
+ description: "Browsers to run tests on. On a mobile (`android`/`ios`) platform entry, the browser runs on the managed device: `chrome` on Android, `safari` on iOS (other combinations skip the context), and it fills in automatically when omitted. Device browsers don't take desktop display config: authored `window`/`viewport` dimensions and `headless: false` are rejected on mobile entries (the device owns its display \u2014 control it via the device descriptor's `headless`/`deviceType`); `headless: true` matches this schema's default and is ignored.",
487810
487990
  anyOf: [
487811
487991
  {
487812
487992
  type: "string",
@@ -487817,7 +487997,7 @@ var init_schemas = __esm({
487817
487997
  "safari",
487818
487998
  "webkit"
487819
487999
  ],
487820
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488000
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
487821
488001
  },
487822
488002
  {
487823
488003
  type: "object",
@@ -487836,7 +488016,7 @@ var init_schemas = __esm({
487836
488016
  "safari",
487837
488017
  "webkit"
487838
488018
  ],
487839
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488019
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
487840
488020
  },
487841
488021
  explicit: {
487842
488022
  type: "boolean",
@@ -487896,7 +488076,7 @@ var init_schemas = __esm({
487896
488076
  "safari",
487897
488077
  "webkit"
487898
488078
  ],
487899
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488079
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
487900
488080
  },
487901
488081
  {
487902
488082
  type: "object",
@@ -487915,7 +488095,7 @@ var init_schemas = __esm({
487915
488095
  "safari",
487916
488096
  "webkit"
487917
488097
  ],
487918
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488098
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
487919
488099
  },
487920
488100
  explicit: {
487921
488101
  type: "boolean",
@@ -488201,7 +488381,7 @@ var init_schemas = __esm({
488201
488381
  "safari",
488202
488382
  "webkit"
488203
488383
  ],
488204
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488384
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
488205
488385
  },
488206
488386
  browser: {
488207
488387
  type: "object",
@@ -488220,7 +488400,7 @@ var init_schemas = __esm({
488220
488400
  "safari",
488221
488401
  "webkit"
488222
488402
  ],
488223
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
488403
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
488224
488404
  },
488225
488405
  explicit: {
488226
488406
  type: "boolean",
@@ -488360,6 +488540,26 @@ var init_schemas = __esm({
488360
488540
  }
488361
488541
  }
488362
488542
  ]
488543
+ },
488544
+ {
488545
+ platforms: "android"
488546
+ },
488547
+ {
488548
+ platforms: [
488549
+ "windows",
488550
+ "mac",
488551
+ "android",
488552
+ "ios"
488553
+ ],
488554
+ browsers: "chrome"
488555
+ },
488556
+ {
488557
+ platforms: "ios",
488558
+ browsers: "safari",
488559
+ device: {
488560
+ name: "test-iphone",
488561
+ deviceType: "phone"
488562
+ }
488363
488563
  }
488364
488564
  ]
488365
488565
  }
@@ -530521,7 +530721,7 @@ var init_schemas = __esm({
530521
530721
  "safari",
530522
530722
  "webkit"
530523
530723
  ],
530524
- $comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
530724
+ $comment: "On desktop platforms `safari` is a shortcut for the `webkit` engine. On an `ios` platform entry, `safari` means the real Safari browser on the managed simulator (`webkit` is not supported there). On `android`, `chrome` is the supported browser."
530525
530725
  },
530526
530726
  explicit: {
530527
530727
  type: "boolean",
@@ -585495,6 +585695,41 @@ function ensureRuntimePackageJson(runtimeDir) {
585495
585695
  import_node_fs3.default.writeFileSync(pkgPath, RUNTIME_PACKAGE_JSON_CONTENTS, "utf8");
585496
585696
  }
585497
585697
  }
585698
+ function recordRuntimeDependencies(runtimeDir, ctx) {
585699
+ try {
585700
+ const pkgPath = import_node_path3.default.join(runtimeDir, "package.json");
585701
+ let pkg;
585702
+ try {
585703
+ pkg = JSON.parse(import_node_fs3.default.readFileSync(pkgPath, "utf8"));
585704
+ } catch {
585705
+ pkg = JSON.parse(RUNTIME_PACKAGE_JSON_CONTENTS);
585706
+ }
585707
+ const prior = pkg.dependencies && typeof pkg.dependencies === "object" ? pkg.dependencies : {};
585708
+ const record = readInstalledRecord(ctx);
585709
+ const names = /* @__PURE__ */ new Set([
585710
+ ...Object.keys(record.npmPackages),
585711
+ ...Object.keys(prior)
585712
+ ]);
585713
+ const deps = {};
585714
+ for (const name of [...names].sort()) {
585715
+ if (!import_node_fs3.default.existsSync(import_node_path3.default.join(runtimeDir, "node_modules", name, "package.json"))) {
585716
+ continue;
585717
+ }
585718
+ let range = null;
585719
+ try {
585720
+ range = getDeclaredVersion(name);
585721
+ } catch {
585722
+ const v = readInstalledVersionFromCache(name, ctx);
585723
+ range = prior[name] ?? (v ? `^${v}` : null);
585724
+ }
585725
+ if (range)
585726
+ deps[name] = range;
585727
+ }
585728
+ pkg.dependencies = deps;
585729
+ import_node_fs3.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf8");
585730
+ } catch {
585731
+ }
585732
+ }
585498
585733
  function readInstalledVersionFromCache(name, ctx = {}) {
585499
585734
  const runtimeDir = getRuntimeDir(ctx);
585500
585735
  const candidate = import_node_path3.default.join(runtimeDir, "node_modules", name, "package.json");
@@ -585533,6 +585768,7 @@ async function ensureRuntimeInstalled(packages, options = {}) {
585533
585768
  const specs = toInstall.map((name) => `${name}@${getDeclaredVersion(name)}`);
585534
585769
  const runtimeDir = getRuntimeDir(ctx);
585535
585770
  ensureRuntimePackageJson(runtimeDir);
585771
+ recordRuntimeDependencies(runtimeDir, ctx);
585536
585772
  logger("Installing dependencies\u2026", "info");
585537
585773
  const npmExe = process.platform === "win32" ? "npm.cmd" : "npm";
585538
585774
  const args = [
@@ -585654,6 +585890,7 @@ async function ensureRuntimeInstalled(packages, options = {}) {
585654
585890
  record.npmPackages[name] = { installedVersion, installedAt: now };
585655
585891
  }
585656
585892
  writeInstalledRecord(record, ctx);
585893
+ recordRuntimeDependencies(runtimeDir, ctx);
585657
585894
  }
585658
585895
  var import_node_path3, import_node_fs3, import_node_child_process, import_node_url3, import_node_module, RUNTIME_DEBUG, defaultLogger, requireFromShim, DEFAULT_INSTALL_TIMEOUT_MS, RUNTIME_PACKAGE_JSON_CONTENTS;
585659
585896
  var init_loader = __esm({
@@ -585687,6 +585924,101 @@ var init_loader = __esm({
585687
585924
  }
585688
585925
  });
585689
585926
 
585927
+ // dist/core/ptyWatchdog.js
585928
+ function defaultCreateWorker(data) {
585929
+ const url = new URL("./ptyProbeWorker.js", importMetaUrl);
585930
+ return new import_node_worker_threads.Worker(url, { workerData: data });
585931
+ }
585932
+ function probePtyAllocation(opts) {
585933
+ const timeoutMs = opts.timeoutMs ?? PTY_PROBE_TIMEOUT_MS;
585934
+ const createWorker = opts.createWorker ?? defaultCreateWorker;
585935
+ let worker;
585936
+ try {
585937
+ worker = createWorker({ ptyModulePath: opts.ptyModulePath });
585938
+ } catch (error) {
585939
+ return Promise.resolve({
585940
+ outcome: "inconclusive",
585941
+ detail: `worker-unavailable: ${error?.message ?? error}`
585942
+ });
585943
+ }
585944
+ return new Promise((resolve) => {
585945
+ let settled = false;
585946
+ const terminate = () => {
585947
+ try {
585948
+ Promise.resolve(worker.terminate()).catch(() => {
585949
+ });
585950
+ } catch {
585951
+ }
585952
+ };
585953
+ const finish = (outcome, detail) => {
585954
+ if (settled)
585955
+ return;
585956
+ settled = true;
585957
+ clearTimeout(timer);
585958
+ resolve({ outcome, detail });
585959
+ };
585960
+ const timer = setTimeout(() => {
585961
+ terminate();
585962
+ finish("wedged", `no response in ${timeoutMs}ms`);
585963
+ }, timeoutMs);
585964
+ if (typeof timer.unref === "function")
585965
+ timer.unref();
585966
+ worker.on("message", (msg) => {
585967
+ terminate();
585968
+ if (msg && msg.ok === true)
585969
+ finish("healthy");
585970
+ else
585971
+ finish("inconclusive", msg?.error ? String(msg.error) : "probe reported not-ok");
585972
+ });
585973
+ worker.on("error", (err) => {
585974
+ finish("inconclusive", `worker-error: ${err?.message ?? err}`);
585975
+ });
585976
+ worker.on("exit", () => {
585977
+ finish("inconclusive", "worker exited without a result");
585978
+ });
585979
+ });
585980
+ }
585981
+ async function ensurePtyBackendOnDisk(opts) {
585982
+ const exists = opts.exists ?? import_node_fs4.default.existsSync;
585983
+ const first = opts.resolvePath();
585984
+ if (first && exists(first))
585985
+ return first;
585986
+ try {
585987
+ await opts.reinstall();
585988
+ } catch (error) {
585989
+ const err2 = new Error(`The PTY backend's files are missing from the runtime cache (a JIT install of another dependency previously pruned them \u2014 doc-detective issue #501) and reinstalling failed: ${error?.message ?? error}. Skipping this \`tty\` background process.`);
585990
+ err2.code = "NODE_PTY_UNAVAILABLE";
585991
+ throw err2;
585992
+ }
585993
+ const healed = opts.resolvePath();
585994
+ if (healed && exists(healed))
585995
+ return healed;
585996
+ const err = new Error(`The PTY backend's files are missing from the runtime cache (doc-detective issue #501) and did not materialize after a reinstall. Skipping this \`tty\` background process.`);
585997
+ err.code = "NODE_PTY_UNAVAILABLE";
585998
+ throw err;
585999
+ }
586000
+ async function assertConptyAllocatable(opts) {
586001
+ const platform = opts.platform ?? process.platform;
586002
+ if (platform !== "win32" || !opts.ptyModulePath)
586003
+ return;
586004
+ const probe = opts.probe ?? probePtyAllocation;
586005
+ const result = await probe({ ptyModulePath: opts.ptyModulePath });
586006
+ if (result.outcome === "wedged") {
586007
+ const err = new Error(`ConPTY allocation is wedged on this Windows environment (${result.detail}). This matches doc-detective issue #501: a native app-surface (NovaWindows) context in the same run can leave the console subsystem unable to allocate a new pseudo-terminal, which otherwise freezes the whole process. Skipping this \`tty\` background process instead of hanging the run.`);
586008
+ err.code = "NODE_PTY_UNAVAILABLE";
586009
+ throw err;
586010
+ }
586011
+ }
586012
+ var import_node_fs4, import_node_worker_threads, PTY_PROBE_TIMEOUT_MS;
586013
+ var init_ptyWatchdog = __esm({
586014
+ "dist/core/ptyWatchdog.js"() {
586015
+ "use strict";
586016
+ import_node_fs4 = __toESM(require("node:fs"), 1);
586017
+ import_node_worker_threads = require("node:worker_threads");
586018
+ PTY_PROBE_TIMEOUT_MS = Number(process.env.DOC_DETECTIVE_PTY_PROBE_TIMEOUT_MS) || 15e3;
586019
+ }
586020
+ });
586021
+
585690
586022
  // dist/core/utils.js
585691
586023
  function createAppiumPool(ports) {
585692
586024
  const available = [...ports];
@@ -585874,6 +586206,14 @@ async function spawnPtyBackgroundCommand(cmd, args = [], options = {}) {
585874
586206
  err.code = "NODE_PTY_UNAVAILABLE";
585875
586207
  throw err;
585876
586208
  }
586209
+ const ptyModulePath = await ensurePtyBackendOnDisk({
586210
+ resolvePath: () => resolveHeavyDepPath(PTY_PACKAGE, { cacheDir: options.cacheDir }),
586211
+ reinstall: () => ensureRuntimeInstalled([PTY_PACKAGE], {
586212
+ ctx: { cacheDir: options.cacheDir },
586213
+ force: true
586214
+ })
586215
+ });
586216
+ await assertConptyAllocatable({ ptyModulePath });
585877
586217
  const argstr = args.length ? " " + args.map(quoteShellArg).join(" ") : "";
585878
586218
  const fullCommand = cmd + argstr;
585879
586219
  const isWin = process.platform === "win32";
@@ -586132,18 +586472,18 @@ function appendQueryParams(url, params) {
586132
586472
  }
586133
586473
  function cleanTemp() {
586134
586474
  const tempDir = import_node_path4.default.join(import_node_os2.default.tmpdir(), "doc-detective");
586135
- if (!import_node_fs4.default.existsSync(tempDir))
586475
+ if (!import_node_fs5.default.existsSync(tempDir))
586136
586476
  return;
586137
- for (const entry of import_node_fs4.default.readdirSync(tempDir)) {
586477
+ for (const entry of import_node_fs5.default.readdirSync(tempDir)) {
586138
586478
  if (PRESERVED_TEMP_ENTRIES.has(entry))
586139
586479
  continue;
586140
586480
  const curPath = import_node_path4.default.join(tempDir, entry);
586141
586481
  try {
586142
- const stat = import_node_fs4.default.statSync(curPath);
586482
+ const stat = import_node_fs5.default.statSync(curPath);
586143
586483
  if (stat.isDirectory()) {
586144
- import_node_fs4.default.rmSync(curPath, { recursive: true, force: true });
586484
+ import_node_fs5.default.rmSync(curPath, { recursive: true, force: true });
586145
586485
  } else {
586146
- import_node_fs4.default.unlinkSync(curPath);
586486
+ import_node_fs5.default.unlinkSync(curPath);
586147
586487
  }
586148
586488
  } catch {
586149
586489
  }
@@ -586288,11 +586628,11 @@ async function fetchFile(fileURL, opts = {}) {
586288
586628
  message: new Error(`Refusing to write outside temp dir: ${resolvedFile}`)
586289
586629
  };
586290
586630
  }
586291
- if (!import_node_fs4.default.existsSync(ddTempDir)) {
586292
- import_node_fs4.default.mkdirSync(ddTempDir, { recursive: true });
586631
+ if (!import_node_fs5.default.existsSync(ddTempDir)) {
586632
+ import_node_fs5.default.mkdirSync(ddTempDir, { recursive: true });
586293
586633
  }
586294
- if (!import_node_fs4.default.existsSync(filePath)) {
586295
- import_node_fs4.default.writeFileSync(filePath, data);
586634
+ if (!import_node_fs5.default.existsSync(filePath)) {
586635
+ import_node_fs5.default.writeFileSync(filePath, data);
586296
586636
  }
586297
586637
  return { result: "success", path: filePath };
586298
586638
  } catch (error) {
@@ -586300,7 +586640,7 @@ async function fetchFile(fileURL, opts = {}) {
586300
586640
  }
586301
586641
  }
586302
586642
  async function loadEnvs(envsFile) {
586303
- const fileExists = import_node_fs4.default.existsSync(envsFile);
586643
+ const fileExists = import_node_fs5.default.existsSync(envsFile);
586304
586644
  if (fileExists) {
586305
586645
  const { default: dotenv } = await import("dotenv");
586306
586646
  dotenv.config({ path: envsFile, override: true });
@@ -586375,7 +586715,7 @@ function commandOnPath(command, env, existsSync2) {
586375
586715
  }
586376
586716
  function evaluateContextRequirements({ requires, deps = {} }) {
586377
586717
  const env = deps.env ?? process.env;
586378
- const existsSync2 = deps.existsSync ?? import_node_fs4.default.existsSync;
586718
+ const existsSync2 = deps.existsSync ?? import_node_fs5.default.existsSync;
586379
586719
  const platform = deps.platform ?? process.platform;
586380
586720
  const commandExists = deps.commandExists ?? ((command) => commandOnPath(command, env, existsSync2));
586381
586721
  const isObjectForm = requires && typeof requires === "object" && !Array.isArray(requires);
@@ -586446,7 +586786,7 @@ function getOrInitRunTimestamp(config) {
586446
586786
  function getRunOutputDir(config, { create = true } = {}) {
586447
586787
  if (config?.__runOutputDir) {
586448
586788
  if (create)
586449
- import_node_fs4.default.mkdirSync(config.__runOutputDir, { recursive: true });
586789
+ import_node_fs5.default.mkdirSync(config.__runOutputDir, { recursive: true });
586450
586790
  return config.__runOutputDir;
586451
586791
  }
586452
586792
  let base = String(config?.output || ".");
@@ -586458,11 +586798,11 @@ function getRunOutputDir(config, { create = true } = {}) {
586458
586798
  const runId = getOrInitRunTimestamp(config);
586459
586799
  let dir = import_node_path4.default.join(runsRoot, runId);
586460
586800
  if (create) {
586461
- import_node_fs4.default.mkdirSync(runsRoot, { recursive: true });
586801
+ import_node_fs5.default.mkdirSync(runsRoot, { recursive: true });
586462
586802
  let suffix = 2;
586463
586803
  for (; ; ) {
586464
586804
  try {
586465
- import_node_fs4.default.mkdirSync(dir);
586805
+ import_node_fs5.default.mkdirSync(dir);
586466
586806
  break;
586467
586807
  } catch (error) {
586468
586808
  if (error?.code !== "EEXIST")
@@ -586582,11 +586922,11 @@ function llevenshteinDistance(s, t) {
586582
586922
  }
586583
586923
  return arr[t.length][s.length];
586584
586924
  }
586585
- var import_node_fs4, import_node_os2, import_node_path4, import_node_crypto, import_promises, import_node_net, import_axios, import_node_child_process2, BACKGROUND_BUFFER_LIMIT, PTY_PACKAGE, READY_POLL_INTERVAL_MS, PRESERVED_TEMP_ENTRIES, FETCH_BINARY_DEFAULTS;
586925
+ var import_node_fs5, import_node_os2, import_node_path4, import_node_crypto, import_promises, import_node_net, import_axios, import_node_child_process2, BACKGROUND_BUFFER_LIMIT, PTY_PACKAGE, READY_POLL_INTERVAL_MS, PRESERVED_TEMP_ENTRIES, FETCH_BINARY_DEFAULTS;
586586
586926
  var init_utils = __esm({
586587
586927
  "dist/core/utils.js"() {
586588
586928
  "use strict";
586589
- import_node_fs4 = __toESM(require("node:fs"), 1);
586929
+ import_node_fs5 = __toESM(require("node:fs"), 1);
586590
586930
  import_node_os2 = __toESM(require("node:os"), 1);
586591
586931
  import_node_path4 = __toESM(require("node:path"), 1);
586592
586932
  import_node_crypto = __toESM(require("node:crypto"), 1);
@@ -586595,6 +586935,7 @@ var init_utils = __esm({
586595
586935
  import_axios = __toESM(require("axios"), 1);
586596
586936
  import_node_child_process2 = require("node:child_process");
586597
586937
  init_loader();
586938
+ init_ptyWatchdog();
586598
586939
  BACKGROUND_BUFFER_LIMIT = 256 * 1024;
586599
586940
  PTY_PACKAGE = "@homebridge/node-pty-prebuilt-multiarch";
586600
586941
  READY_POLL_INTERVAL_MS = 500;
@@ -586749,12 +587090,12 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586749
587090
  }
586750
587091
  if (!force && existing && isStillFresh(existing.latestCheckedAt, now)) {
586751
587092
  if (existing.latestKnownVersion === existing.installedVersion) {
586752
- const path26 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
586753
- return { path: path26, version: existing.installedVersion, outdated: false };
587093
+ const path27 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
587094
+ return { path: path27, version: existing.installedVersion, outdated: false };
586754
587095
  }
586755
- const path25 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
587096
+ const path26 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
586756
587097
  logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${existing.latestKnownVersion}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
586757
- return { path: path25, version: existing.installedVersion, outdated: true };
587098
+ return { path: path26, version: existing.installedVersion, outdated: true };
586758
587099
  }
586759
587100
  let latest;
586760
587101
  try {
@@ -586762,8 +587103,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586762
587103
  } catch (err) {
586763
587104
  logger(`Channel resolution for ${name} skipped: ${String(err)}`, "debug");
586764
587105
  if (existing) {
586765
- const path25 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
586766
- return { path: path25, version: existing.installedVersion, outdated: false };
587106
+ const path26 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
587107
+ return { path: path26, version: existing.installedVersion, outdated: false };
586767
587108
  }
586768
587109
  throw err;
586769
587110
  }
@@ -586774,8 +587115,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586774
587115
  latestCheckedAt: now.toISOString()
586775
587116
  };
586776
587117
  writeInstalledRecord(record, ctx);
586777
- const path25 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
586778
- return { path: path25, version: latest, outdated: false };
587118
+ const path26 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
587119
+ return { path: path26, version: latest, outdated: false };
586779
587120
  }
586780
587121
  if (!force && existing) {
586781
587122
  record.browsers[name] = {
@@ -586785,8 +587126,8 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586785
587126
  };
586786
587127
  writeInstalledRecord(record, ctx);
586787
587128
  logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${latest}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
586788
- const path25 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
586789
- return { path: path25, version: existing.installedVersion, outdated: true };
587129
+ const path26 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
587130
+ return { path: path26, version: existing.installedVersion, outdated: true };
586790
587131
  }
586791
587132
  logger(`Installing ${name} ${latest} into ${cacheDir}`, "info");
586792
587133
  await browsersModule.install({
@@ -586794,9 +587135,9 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586794
587135
  buildId: latest,
586795
587136
  cacheDir
586796
587137
  });
586797
- let path24 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
586798
- if (name === "chromedriver" && isAllowedDriverPath(path24)) {
586799
- let verify = await verifyDriverBinary(name, path24, { exec: deps.verifyExec });
587138
+ let path25 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
587139
+ if (name === "chromedriver" && isAllowedDriverPath(path25)) {
587140
+ let verify = await verifyDriverBinary(name, path25, { exec: deps.verifyExec });
586800
587141
  if (!verify.ok) {
586801
587142
  logger(`Installed ${name} ${latest} failed validation (${verify.error}); reinstalling once.`, "warn");
586802
587143
  try {
@@ -586805,11 +587146,11 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586805
587146
  logger(`Failed to prune broken ${name} ${latest}: ${String(err)}`, "debug");
586806
587147
  }
586807
587148
  await browsersModule.install({ browser: name, buildId: latest, cacheDir });
586808
- path24 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
586809
- verify = await verifyDriverBinary(name, path24, { exec: deps.verifyExec });
587149
+ path25 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
587150
+ verify = await verifyDriverBinary(name, path25, { exec: deps.verifyExec });
586810
587151
  }
586811
587152
  if (!verify.ok) {
586812
- throw new Error(`${name} ${latest} is present but non-functional after a reinstall (${verify.error}). It may be a partial or corrupt download; delete ${path24} or reinstall.`);
587153
+ throw new Error(`${name} ${latest} is present but non-functional after a reinstall (${verify.error}). It may be a partial or corrupt download; delete ${path25} or reinstall.`);
586813
587154
  }
586814
587155
  }
586815
587156
  if (existing && existing.installedVersion !== latest) {
@@ -586830,7 +587171,7 @@ async function ensureBrowserInstalledImpl(name, options = {}) {
586830
587171
  latestCheckedAt: now.toISOString()
586831
587172
  };
586832
587173
  writeInstalledRecord(record, ctx);
586833
- return { path: path24, version: latest, outdated: false };
587174
+ return { path: path25, version: latest, outdated: false };
586834
587175
  }
586835
587176
  async function locateExecutable(browsersModule, name, buildId, cacheDir, platform) {
586836
587177
  if (typeof browsersModule.computeExecutablePath === "function") {
@@ -586871,16 +587212,16 @@ function geckodriverBinaryInCache(cacheDir) {
586871
587212
  const binName = process.platform === "win32" ? "geckodriver.exe" : "geckodriver";
586872
587213
  const rootCandidate = import_node_path5.default.join(cacheDir, binName);
586873
587214
  try {
586874
- if (import_node_fs5.default.existsSync(rootCandidate))
587215
+ if (import_node_fs6.default.existsSync(rootCandidate))
586875
587216
  return rootCandidate;
586876
587217
  } catch {
586877
587218
  }
586878
587219
  try {
586879
- for (const entry of import_node_fs5.default.readdirSync(cacheDir, { withFileTypes: true })) {
587220
+ for (const entry of import_node_fs6.default.readdirSync(cacheDir, { withFileTypes: true })) {
586880
587221
  if (!entry.isDirectory())
586881
587222
  continue;
586882
587223
  const nested = import_node_path5.default.join(cacheDir, entry.name, binName);
586883
- if (import_node_fs5.default.existsSync(nested))
587224
+ if (import_node_fs6.default.existsSync(nested))
586884
587225
  return nested;
586885
587226
  }
586886
587227
  } catch {
@@ -586918,8 +587259,8 @@ async function ensureGeckodriver(ctxBag, record, existing, logger) {
586918
587259
  if (!verify.ok) {
586919
587260
  logger(`Downloaded geckodriver failed validation (${verify.error}); re-downloading once.`, "warn");
586920
587261
  try {
586921
- if (binaryPath && binaryPath !== cacheDir && import_node_fs5.default.existsSync(binaryPath)) {
586922
- import_node_fs5.default.rmSync(binaryPath, { force: true });
587262
+ if (binaryPath && binaryPath !== cacheDir && import_node_fs6.default.existsSync(binaryPath)) {
587263
+ import_node_fs6.default.rmSync(binaryPath, { force: true });
586923
587264
  }
586924
587265
  } catch {
586925
587266
  }
@@ -586956,12 +587297,12 @@ function getInstalledBrowsers(ctx = {}) {
586956
587297
  latestKnownVersion: entry.latestKnownVersion
586957
587298
  }));
586958
587299
  }
586959
- var import_node_child_process3, import_node_fs5, import_node_path5, DRIVER_VERSION_ARGS, DRIVER_VERIFY_TIMEOUT_MS, ALLOWED_DRIVER_PATH, defaultDriverExec, BROWSER_CHANNELS, FRESHNESS_TTL_MS, RESOLVE_TIMEOUT_MS, RUNTIME_DEBUG2, defaultLogger2, inFlightInstalls, geckodriverEnvQueue;
587300
+ var import_node_child_process3, import_node_fs6, import_node_path5, DRIVER_VERSION_ARGS, DRIVER_VERIFY_TIMEOUT_MS, ALLOWED_DRIVER_PATH, defaultDriverExec, BROWSER_CHANNELS, FRESHNESS_TTL_MS, RESOLVE_TIMEOUT_MS, RUNTIME_DEBUG2, defaultLogger2, inFlightInstalls, geckodriverEnvQueue;
586960
587301
  var init_browsers = __esm({
586961
587302
  "dist/runtime/browsers.js"() {
586962
587303
  "use strict";
586963
587304
  import_node_child_process3 = require("node:child_process");
586964
- import_node_fs5 = __toESM(require("node:fs"), 1);
587305
+ import_node_fs6 = __toESM(require("node:fs"), 1);
586965
587306
  import_node_path5 = __toESM(require("node:path"), 1);
586966
587307
  init_cacheDir();
586967
587308
  init_loader();
@@ -587013,13 +587354,19 @@ function appiumHomeForDriverPath(driverEntry) {
587013
587354
  const idx = driverEntry.lastIndexOf(marker);
587014
587355
  return idx === -1 ? null : driverEntry.slice(0, idx);
587015
587356
  }
587357
+ function runtimeHomeHasBrowserDriver(runtimeDir) {
587358
+ const nm = import_node_path6.default.join(runtimeDir, "node_modules");
587359
+ if (!(0, import_node_fs7.existsSync)(import_node_path6.default.join(nm, "appium")))
587360
+ return false;
587361
+ return BROWSER_DRIVER_PACKAGES.some((driver) => (0, import_node_fs7.existsSync)(import_node_path6.default.join(nm, driver)));
587362
+ }
587016
587363
  function setAppiumHome(ctx = {}) {
587017
- const runtimeAppium = import_node_path6.default.join(getRuntimeDir({ cacheDir: ctx.cacheDir }), "node_modules", "appium");
587018
- if ((0, import_node_fs6.existsSync)(runtimeAppium)) {
587019
- process.env.APPIUM_HOME = import_node_path6.default.join(getRuntimeDir({ cacheDir: ctx.cacheDir }));
587364
+ const runtimeDir = getRuntimeDir({ cacheDir: ctx.cacheDir });
587365
+ if (runtimeHomeHasBrowserDriver(runtimeDir)) {
587366
+ process.env.APPIUM_HOME = runtimeDir;
587020
587367
  return;
587021
587368
  }
587022
- for (const driverName of ["appium-chromium-driver", "appium-geckodriver"]) {
587369
+ for (const driverName of BROWSER_DRIVER_PACKAGES) {
587023
587370
  const driverEntry = resolveHeavyDepPath(driverName, {
587024
587371
  cacheDir: ctx.cacheDir
587025
587372
  });
@@ -587033,23 +587380,28 @@ function setAppiumHome(ctx = {}) {
587033
587380
  const pathArray = corePath.split("node_modules");
587034
587381
  let appiumParentPath = pathArray[0];
587035
587382
  for (let i = 1; i < pathArray.length; i++) {
587036
- if ((0, import_node_fs6.existsSync)(import_node_path6.default.join(appiumParentPath, "node_modules", "appium"))) {
587383
+ if ((0, import_node_fs7.existsSync)(import_node_path6.default.join(appiumParentPath, "node_modules", "appium"))) {
587037
587384
  break;
587038
587385
  }
587039
587386
  appiumParentPath = import_node_path6.default.join(appiumParentPath, "node_modules", pathArray[i]);
587040
587387
  }
587041
587388
  process.env.APPIUM_HOME = appiumParentPath;
587042
587389
  }
587043
- var import_node_path6, import_node_fs6, import_node_url4, __dirname3;
587390
+ var import_node_path6, import_node_fs7, import_node_url4, __dirname3, BROWSER_DRIVER_PACKAGES;
587044
587391
  var init_appium = __esm({
587045
587392
  "dist/core/appium.js"() {
587046
587393
  "use strict";
587047
587394
  import_node_path6 = __toESM(require("node:path"), 1);
587048
- import_node_fs6 = require("node:fs");
587395
+ import_node_fs7 = require("node:fs");
587049
587396
  import_node_url4 = require("node:url");
587050
587397
  init_cacheDir();
587051
587398
  init_loader();
587052
587399
  __dirname3 = import_node_path6.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
587400
+ BROWSER_DRIVER_PACKAGES = [
587401
+ "appium-chromium-driver",
587402
+ "appium-geckodriver",
587403
+ "appium-safari-driver"
587404
+ ];
587053
587405
  }
587054
587406
  });
587055
587407
 
@@ -587081,7 +587433,7 @@ async function readFile({ fileURLOrPath }) {
587081
587433
  }
587082
587434
  } else {
587083
587435
  try {
587084
- content = await import_node_fs7.default.promises.readFile(fileURLOrPath, "utf8");
587436
+ content = await import_node_fs8.default.promises.readFile(fileURLOrPath, "utf8");
587085
587437
  } catch (error) {
587086
587438
  if (error.code === "ENOENT") {
587087
587439
  console.warn(`File not found: ${fileURLOrPath}`);
@@ -587155,8 +587507,8 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
587155
587507
  if (import_node_path7.default.isAbsolute(relativePath)) {
587156
587508
  return relativePath;
587157
587509
  }
587158
- const fileExists = import_node_fs7.default.existsSync(filePath2);
587159
- const isFile = fileExists ? import_node_fs7.default.lstatSync(filePath2).isFile() : import_node_path7.default.parse(filePath2).ext !== "";
587510
+ const fileExists = import_node_fs8.default.existsSync(filePath2);
587511
+ const isFile = fileExists ? import_node_fs8.default.lstatSync(filePath2).isFile() : import_node_path7.default.parse(filePath2).ext !== "";
587160
587512
  const basePath = isFile ? import_node_path7.default.dirname(filePath2) : filePath2;
587161
587513
  return baseType === "file" ? import_node_path7.default.resolve(basePath, relativePath) : import_node_path7.default.resolve(relativePath);
587162
587514
  }
@@ -587235,11 +587587,11 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
587235
587587
  }
587236
587588
  return object;
587237
587589
  }
587238
- var import_node_fs7, import_node_path7, import_yaml, import_axios2;
587590
+ var import_node_fs8, import_node_path7, import_yaml, import_axios2;
587239
587591
  var init_files = __esm({
587240
587592
  "dist/core/files.js"() {
587241
587593
  "use strict";
587242
- import_node_fs7 = __toESM(require("node:fs"), 1);
587594
+ import_node_fs8 = __toESM(require("node:fs"), 1);
587243
587595
  import_node_path7 = __toESM(require("node:path"), 1);
587244
587596
  import_yaml = __toESM(require("yaml"), 1);
587245
587597
  import_axios2 = __toESM(require("axios"), 1);
@@ -587263,10 +587615,10 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
587263
587615
  if (!operationId) {
587264
587616
  throw new Error("OperationId is required.");
587265
587617
  }
587266
- for (const path24 in definition.paths) {
587267
- for (const method in definition.paths[path24]) {
587268
- if (definition.paths[path24][method].operationId === operationId) {
587269
- const operation = definition.paths[path24][method];
587618
+ for (const path25 in definition.paths) {
587619
+ for (const method in definition.paths[path25]) {
587620
+ if (definition.paths[path25][method].operationId === operationId) {
587621
+ const operation = definition.paths[path25][method];
587270
587622
  if (!server) {
587271
587623
  if (definition.servers && definition.servers.length > 0) {
587272
587624
  server = definition.servers[0].url;
@@ -587274,9 +587626,9 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
587274
587626
  throw new Error("No server URL provided and no servers defined in the OpenAPI definition.");
587275
587627
  }
587276
587628
  }
587277
- const example = await compileExample(operation, server + path24, responseCode, exampleKey);
587629
+ const example = await compileExample(operation, server + path25, responseCode, exampleKey);
587278
587630
  const schemas2 = getSchemas(operation, responseCode);
587279
- return { path: path24, method, definition: operation, schemas: schemas2, example };
587631
+ return { path: path25, method, definition: operation, schemas: schemas2, example };
587280
587632
  }
587281
587633
  }
587282
587634
  }
@@ -587297,15 +587649,15 @@ function getSchemas(definition = {}, responseCode = "") {
587297
587649
  schemas2.response = definition.responses[responseCode].content[Object.keys(definition.responses[responseCode].content)[0]].schema;
587298
587650
  return schemas2;
587299
587651
  }
587300
- async function compileExample(operation = {}, path24 = "", responseCode = "", exampleKey = "") {
587652
+ async function compileExample(operation = {}, path25 = "", responseCode = "", exampleKey = "") {
587301
587653
  if (!operation) {
587302
587654
  throw new Error("Operation is required.");
587303
587655
  }
587304
- if (!path24) {
587656
+ if (!path25) {
587305
587657
  throw new Error("Path is required.");
587306
587658
  }
587307
587659
  let example = {
587308
- url: path24,
587660
+ url: path25,
587309
587661
  request: { parameters: {}, headers: {}, body: {} },
587310
587662
  response: { headers: {}, body: {} }
587311
587663
  };
@@ -587900,7 +588252,7 @@ async function getBrowserDiagnostics({ config }) {
587900
588252
  const version = await probeSafariVersion(4e3);
587901
588253
  safariApp = version !== null;
587902
588254
  safariVersion = version ?? void 0;
587903
- safaridriver = import_node_fs8.default.existsSync("/usr/bin/safaridriver");
588255
+ safaridriver = import_node_fs9.default.existsSync("/usr/bin/safaridriver");
587904
588256
  } catch {
587905
588257
  detectionFailed = true;
587906
588258
  }
@@ -587922,12 +588274,12 @@ async function getBrowserDiagnostics({ config }) {
587922
588274
  });
587923
588275
  return { browsers, detectionFailed };
587924
588276
  }
587925
- var import_node_os3, import_node_fs8, import_node_path8, import_node_child_process4, import_node_url5, __dirname4, platformMap, defaultFileTypes, cachedAppsByDir;
588277
+ var import_node_os3, import_node_fs9, import_node_path8, import_node_child_process4, import_node_url5, __dirname4, platformMap, defaultFileTypes, cachedAppsByDir;
587926
588278
  var init_config = __esm({
587927
588279
  "dist/core/config.js"() {
587928
588280
  "use strict";
587929
588281
  import_node_os3 = __toESM(require("node:os"), 1);
587930
- import_node_fs8 = __toESM(require("node:fs"), 1);
588282
+ import_node_fs9 = __toESM(require("node:fs"), 1);
587931
588283
  init_validate();
587932
588284
  init_utils();
587933
588285
  import_node_path8 = __toESM(require("node:path"), 1);
@@ -588377,7 +588729,7 @@ module.exports = __toCommonJS(index_exports);
588377
588729
  init_config();
588378
588730
 
588379
588731
  // dist/core/detectTests.js
588380
- var import_node_fs10 = __toESM(require("node:fs"), 1);
588732
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
588381
588733
  var import_node_path10 = __toESM(require("node:path"), 1);
588382
588734
  var import_node_os5 = __toESM(require("node:os"), 1);
588383
588735
  var import_node_crypto3 = __toESM(require("node:crypto"), 1);
@@ -589251,7 +589603,7 @@ init_files();
589251
589603
  init_utils();
589252
589604
 
589253
589605
  // dist/core/integrations/heretto.js
589254
- var import_node_fs9 = __toESM(require("node:fs"), 1);
589606
+ var import_node_fs10 = __toESM(require("node:fs"), 1);
589255
589607
  var import_node_path9 = __toESM(require("node:path"), 1);
589256
589608
  var import_node_https = __toESM(require("node:https"), 1);
589257
589609
  var import_node_http = __toESM(require("node:http"), 1);
@@ -589412,11 +589764,11 @@ var HerettoUploader = class {
589412
589764
  return result;
589413
589765
  }
589414
589766
  }
589415
- if (!import_node_fs9.default.existsSync(localFilePath)) {
589767
+ if (!import_node_fs10.default.existsSync(localFilePath)) {
589416
589768
  result.description = `Local file not found: ${localFilePath}`;
589417
589769
  return result;
589418
589770
  }
589419
- const fileContent = import_node_fs9.default.readFileSync(localFilePath);
589771
+ const fileContent = import_node_fs10.default.readFileSync(localFilePath);
589420
589772
  const contentType = this.getContentType(localFilePath);
589421
589773
  try {
589422
589774
  await this.uploadFile({
@@ -590190,7 +590542,7 @@ async function pollJobStatus(client, fileId, jobId, log3, config) {
590190
590542
  return null;
590191
590543
  }
590192
590544
  async function downloadAndExtractOutput(client, fileId, jobId, herettoName, log3, config, deps) {
590193
- const fsModule = deps?.fsModule || import_node_fs9.default;
590545
+ const fsModule = deps?.fsModule || import_node_fs10.default;
590194
590546
  const ZipClass = deps?.ZipClass || import_adm_zip.default;
590195
590547
  const tempDir = import_node_path9.default.join(import_node_os4.default.tmpdir(), "doc-detective");
590196
590548
  const hash = import_node_crypto2.default.createHash("md5").update(`${herettoName}_${jobId}`).digest("hex");
@@ -590425,7 +590777,7 @@ async function isValidSourceFile({ config, files, source }) {
590425
590777
  } else {
590426
590778
  beforePath = import_node_path10.default.resolve(test.before);
590427
590779
  }
590428
- if (!import_node_fs10.default.existsSync(beforePath)) {
590780
+ if (!import_node_fs11.default.existsSync(beforePath)) {
590429
590781
  log(config, "debug", `${beforePath} is specified to run before a test but isn't a valid file. Skipping ${source}.`);
590430
590782
  return false;
590431
590783
  }
@@ -590437,7 +590789,7 @@ async function isValidSourceFile({ config, files, source }) {
590437
590789
  } else {
590438
590790
  afterPath = import_node_path10.default.resolve(test.after);
590439
590791
  }
590440
- if (!import_node_fs10.default.existsSync(afterPath)) {
590792
+ if (!import_node_fs11.default.existsSync(afterPath)) {
590441
590793
  log(config, "debug", `${afterPath} is specified to run after a test but isn't a valid file. Skipping ${source}.`);
590442
590794
  return false;
590443
590795
  }
@@ -590455,9 +590807,9 @@ async function processDitaMap({ config, source }) {
590455
590807
  const hash = import_node_crypto3.default.createHash("md5").update(source).digest("hex");
590456
590808
  const tmpBase = import_node_path10.default.join(import_node_os5.default.tmpdir(), "doc-detective");
590457
590809
  const outputDir = import_node_path10.default.join(tmpBase, `ditamap_${hash}`);
590458
- if (!import_node_fs10.default.existsSync(tmpBase)) {
590810
+ if (!import_node_fs11.default.existsSync(tmpBase)) {
590459
590811
  log(config, "debug", `Creating temp directory: ${tmpBase}`);
590460
- import_node_fs10.default.mkdirSync(tmpBase, { recursive: true });
590812
+ import_node_fs11.default.mkdirSync(tmpBase, { recursive: true });
590461
590813
  }
590462
590814
  const ditaVersion = await spawnCommand("dita", ["--version"]);
590463
590815
  if (ditaVersion.exitCode !== 0) {
@@ -590564,8 +590916,8 @@ async function qualifyFiles({ config }) {
590564
590916
  let isFile = false;
590565
590917
  let isDir = false;
590566
590918
  try {
590567
- isFile = import_node_fs10.default.statSync(source).isFile();
590568
- isDir = import_node_fs10.default.statSync(source).isDirectory();
590919
+ isFile = import_node_fs11.default.statSync(source).isFile();
590920
+ isDir = import_node_fs11.default.statSync(source).isDirectory();
590569
590921
  } catch {
590570
590922
  log(config, "warning", `Cannot access path: ${source}. Skipping.`);
590571
590923
  continue;
@@ -590588,13 +590940,13 @@ async function qualifyFiles({ config }) {
590588
590940
  dirs = [];
590589
590941
  dirs[0] = source;
590590
590942
  for (const dir of dirs) {
590591
- const objects = import_node_fs10.default.readdirSync(dir);
590943
+ const objects = import_node_fs11.default.readdirSync(dir);
590592
590944
  for (const object of objects) {
590593
590945
  const content = import_node_path10.default.resolve(dir + "/" + object);
590594
590946
  if (content.includes("node_modules"))
590595
590947
  continue;
590596
- const isFile2 = import_node_fs10.default.statSync(content).isFile();
590597
- const isDir2 = import_node_fs10.default.statSync(content).isDirectory();
590948
+ const isFile2 = import_node_fs11.default.statSync(content).isFile();
590949
+ const isDir2 = import_node_fs11.default.statSync(content).isDirectory();
590598
590950
  if (isFile2 && await isValidSourceFile({ config, files, source: content })) {
590599
590951
  const resolved2 = import_node_path10.default.resolve(content);
590600
590952
  files.push(resolved2);
@@ -590618,7 +590970,7 @@ async function parseTests({ config, files }) {
590618
590970
  let rawContent;
590619
590971
  if (extension === "json" || extension === "yaml" || extension === "yml") {
590620
590972
  try {
590621
- rawContent = await import_node_fs10.default.promises.readFile(file, "utf8");
590973
+ rawContent = await import_node_fs11.default.promises.readFile(file, "utf8");
590622
590974
  if (extension === "json") {
590623
590975
  content = JSON.parse(rawContent);
590624
590976
  } else {
@@ -590882,6 +591234,15 @@ var import_node_crypto4 = __toESM(require("node:crypto"), 1);
590882
591234
  init_utils();
590883
591235
  init_openapi();
590884
591236
  init_browserStepKeys();
591237
+
591238
+ // dist/core/tests/mobilePlatform.js
591239
+ function isMobileTargetPlatform(platform) {
591240
+ if (platform === "android" || platform === "ios")
591241
+ return platform;
591242
+ return null;
591243
+ }
591244
+
591245
+ // dist/core/resolveTests.js
590885
591246
  function resolveContexts({ contexts, test, config }) {
590886
591247
  log(config, "debug", `Determining required contexts for test: ${test.testId}`);
590887
591248
  const resolvedContexts = [];
@@ -590901,8 +591262,6 @@ function resolveContexts({ contexts, test, config }) {
590901
591262
  if (typeof browser === "string") {
590902
591263
  browser = { name: browser };
590903
591264
  }
590904
- if (browser.name === "safari")
590905
- browser.name = "webkit";
590906
591265
  browser.explicit = true;
590907
591266
  return browser;
590908
591267
  });
@@ -590931,8 +591290,9 @@ function resolveContexts({ contexts, test, config }) {
590931
591290
  const staticContext = { ...carry };
590932
591291
  if (platform !== void 0)
590933
591292
  staticContext.platform = platform;
590934
- if (browser !== void 0)
590935
- staticContext.browser = browser;
591293
+ if (browser !== void 0) {
591294
+ staticContext.browser = browser.name === "safari" && !isMobileTargetPlatform(platform) ? { ...browser, name: "webkit" } : { ...browser };
591295
+ }
590936
591296
  staticContexts.push(staticContext);
590937
591297
  });
590938
591298
  }
@@ -591091,7 +591451,7 @@ init_utils();
591091
591451
 
591092
591452
  // dist/core/tests.js
591093
591453
  var import_tree_kill3 = __toESM(require("tree-kill"), 1);
591094
- var import_node_fs25 = __toESM(require("node:fs"), 1);
591454
+ var import_node_fs26 = __toESM(require("node:fs"), 1);
591095
591455
  init_loader();
591096
591456
  init_browsers();
591097
591457
  init_browserStepKeys();
@@ -591777,7 +592137,7 @@ async function resolveCloseTargets(driver, ref, opts = {}) {
591777
592137
  }
591778
592138
 
591779
592139
  // dist/core/tests/appSurface.js
591780
- var import_node_fs14 = __toESM(require("node:fs"), 1);
592140
+ var import_node_fs15 = __toESM(require("node:fs"), 1);
591781
592141
  var import_node_path13 = __toESM(require("node:path"), 1);
591782
592142
  var import_node_child_process8 = require("node:child_process");
591783
592143
  init_loader();
@@ -591789,7 +592149,7 @@ init_utils();
591789
592149
  var import_node_child_process5 = require("node:child_process");
591790
592150
  var import_node_os6 = __toESM(require("node:os"), 1);
591791
592151
  var import_node_path11 = __toESM(require("node:path"), 1);
591792
- var import_node_fs11 = __toESM(require("node:fs"), 1);
592152
+ var import_node_fs12 = __toESM(require("node:fs"), 1);
591793
592153
  var import_node_crypto5 = __toESM(require("node:crypto"), 1);
591794
592154
  init_loader();
591795
592155
  init_utils();
@@ -592189,7 +592549,7 @@ async function startXvfb(display, opts = {}) {
592189
592549
  if (proc.exitCode !== null)
592190
592550
  throw new Error(`Xvfb exited early on ${display} (code ${proc.exitCode})`);
592191
592551
  try {
592192
- if (import_node_fs11.default.statSync(lock).mtimeMs >= startMs)
592552
+ if (import_node_fs12.default.statSync(lock).mtimeMs >= startMs)
592193
592553
  return proc;
592194
592554
  } catch {
592195
592555
  }
@@ -592206,7 +592566,7 @@ async function startXvfb(display, opts = {}) {
592206
592566
  var import_node_child_process7 = require("node:child_process");
592207
592567
 
592208
592568
  // dist/runtime/androidInstaller.js
592209
- var import_node_fs13 = __toESM(require("node:fs"), 1);
592569
+ var import_node_fs14 = __toESM(require("node:fs"), 1);
592210
592570
  var import_node_os7 = __toESM(require("node:os"), 1);
592211
592571
  var import_node_path12 = __toESM(require("node:path"), 1);
592212
592572
  var import_node_https2 = __toESM(require("node:https"), 1);
@@ -592214,7 +592574,7 @@ var import_node_child_process6 = require("node:child_process");
592214
592574
  init_cacheDir();
592215
592575
 
592216
592576
  // dist/runtime/androidSdk.js
592217
- var import_node_fs12 = __toESM(require("node:fs"), 1);
592577
+ var import_node_fs13 = __toESM(require("node:fs"), 1);
592218
592578
  init_cacheDir();
592219
592579
  function trimTrailingSeparators(s) {
592220
592580
  let end = s.length;
@@ -592258,7 +592618,7 @@ function usable(tools) {
592258
592618
  }
592259
592619
  function detectAndroidSdk(ctx = {}, deps = {}) {
592260
592620
  const env = deps.env ?? process.env;
592261
- const existsSync2 = deps.existsSync ?? import_node_fs12.default.existsSync;
592621
+ const existsSync2 = deps.existsSync ?? import_node_fs13.default.existsSync;
592262
592622
  const platform = deps.platform ?? process.platform;
592263
592623
  const cacheRoot = safeCacheDir(ctx);
592264
592624
  const cacheAndroidSdk = deps.cacheAndroidSdk ?? (cacheRoot ? joinFor(platform, cacheRoot, "android-sdk") : void 0);
@@ -592408,8 +592768,8 @@ function pickSystemImage(images, { osVersion, abi }) {
592408
592768
  return candidates.length > 0 ? candidates[0].pkg : null;
592409
592769
  }
592410
592770
  function listInstalledSystemImages(sdkRoot, deps = {}) {
592411
- const existsSync2 = deps.existsSync ?? import_node_fs13.default.existsSync;
592412
- const readdirSync = deps.readdirSync ?? ((p) => import_node_fs13.default.readdirSync(p));
592771
+ const existsSync2 = deps.existsSync ?? import_node_fs14.default.existsSync;
592772
+ const readdirSync = deps.readdirSync ?? ((p) => import_node_fs14.default.readdirSync(p));
592413
592773
  const base = import_node_path12.default.join(sdkRoot, "system-images");
592414
592774
  if (!existsSync2(base))
592415
592775
  return [];
@@ -592710,7 +593070,7 @@ function realJavaPresent() {
592710
593070
  function androidAvdHome() {
592711
593071
  const home = import_node_path12.default.join(import_node_os7.default.homedir(), ".android", "avd");
592712
593072
  try {
592713
- import_node_fs13.default.mkdirSync(home, { recursive: true });
593073
+ import_node_fs14.default.mkdirSync(home, { recursive: true });
592714
593074
  } catch {
592715
593075
  }
592716
593076
  return home;
@@ -592744,20 +593104,20 @@ async function realRun(command, args, opts = {}) {
592744
593104
  });
592745
593105
  }
592746
593106
  function realDetectCachedJavaHome(cacheJreRoot) {
592747
- if (!import_node_fs13.default.existsSync(cacheJreRoot))
593107
+ if (!import_node_fs14.default.existsSync(cacheJreRoot))
592748
593108
  return null;
592749
- const entries = import_node_fs13.default.readdirSync(cacheJreRoot);
593109
+ const entries = import_node_fs14.default.readdirSync(cacheJreRoot);
592750
593110
  const home = resolveJavaHome(cacheJreRoot, entries, process.platform);
592751
- return home && import_node_fs13.default.existsSync(javaBinPath(home)) ? home : null;
593111
+ return home && import_node_fs14.default.existsSync(javaBinPath(home)) ? home : null;
592752
593112
  }
592753
593113
  async function realBootstrapJava(cacheJreRoot) {
592754
593114
  const url = jreDownloadUrl();
592755
- const tmpDir = import_node_fs13.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "dd-jre-"));
593115
+ const tmpDir = import_node_fs14.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "dd-jre-"));
592756
593116
  try {
592757
593117
  const archive = import_node_path12.default.join(tmpDir, jreArchiveFilename());
592758
593118
  await downloadFile(url, archive);
592759
- import_node_fs13.default.rmSync(cacheJreRoot, { recursive: true, force: true });
592760
- import_node_fs13.default.mkdirSync(cacheJreRoot, { recursive: true });
593119
+ import_node_fs14.default.rmSync(cacheJreRoot, { recursive: true, force: true });
593120
+ import_node_fs14.default.mkdirSync(cacheJreRoot, { recursive: true });
592761
593121
  await extractZip(archive, cacheJreRoot);
592762
593122
  const home = realDetectCachedJavaHome(cacheJreRoot);
592763
593123
  if (!home) {
@@ -592765,31 +593125,31 @@ async function realBootstrapJava(cacheJreRoot) {
592765
593125
  }
592766
593126
  return home;
592767
593127
  } finally {
592768
- import_node_fs13.default.rmSync(tmpDir, { recursive: true, force: true });
593128
+ import_node_fs14.default.rmSync(tmpDir, { recursive: true, force: true });
592769
593129
  }
592770
593130
  }
592771
593131
  async function realBootstrap(url, destSdkRoot) {
592772
- const tmpDir = import_node_fs13.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "dd-android-cli-"));
593132
+ const tmpDir = import_node_fs14.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "dd-android-cli-"));
592773
593133
  try {
592774
593134
  const zipPath = import_node_path12.default.join(tmpDir, "cmdline-tools.zip");
592775
593135
  await downloadFile(url, zipPath);
592776
593136
  const extractDir = import_node_path12.default.join(tmpDir, "extracted");
592777
- import_node_fs13.default.mkdirSync(extractDir, { recursive: true });
593137
+ import_node_fs14.default.mkdirSync(extractDir, { recursive: true });
592778
593138
  await extractZip(zipPath, extractDir);
592779
593139
  const inner = import_node_path12.default.join(extractDir, "cmdline-tools");
592780
- if (!import_node_fs13.default.existsSync(inner)) {
593140
+ if (!import_node_fs14.default.existsSync(inner)) {
592781
593141
  throw new Error("unexpected command-line tools archive layout (no top-level cmdline-tools/)");
592782
593142
  }
592783
593143
  const dest = import_node_path12.default.join(destSdkRoot, "cmdline-tools", "latest");
592784
- import_node_fs13.default.mkdirSync(import_node_path12.default.dirname(dest), { recursive: true });
592785
- import_node_fs13.default.rmSync(dest, { recursive: true, force: true });
593144
+ import_node_fs14.default.mkdirSync(import_node_path12.default.dirname(dest), { recursive: true });
593145
+ import_node_fs14.default.rmSync(dest, { recursive: true, force: true });
592786
593146
  try {
592787
- import_node_fs13.default.renameSync(inner, dest);
593147
+ import_node_fs14.default.renameSync(inner, dest);
592788
593148
  } catch {
592789
- import_node_fs13.default.cpSync(inner, dest, { recursive: true });
593149
+ import_node_fs14.default.cpSync(inner, dest, { recursive: true });
592790
593150
  }
592791
593151
  } finally {
592792
- import_node_fs13.default.rmSync(tmpDir, { recursive: true, force: true });
593152
+ import_node_fs14.default.rmSync(tmpDir, { recursive: true, force: true });
592793
593153
  }
592794
593154
  }
592795
593155
  async function downloadFile(url, dest, redirects = 0) {
@@ -592808,7 +593168,7 @@ async function downloadFile(url, dest, redirects = 0) {
592808
593168
  reject(new Error(`download failed (HTTP ${status}) for ${url}`));
592809
593169
  return;
592810
593170
  }
592811
- const file = import_node_fs13.default.createWriteStream(dest);
593171
+ const file = import_node_fs14.default.createWriteStream(dest);
592812
593172
  res.pipe(file);
592813
593173
  file.on("finish", () => file.close(() => resolve()));
592814
593174
  file.on("error", reject);
@@ -593083,8 +593443,8 @@ async function hostHasKvm() {
593083
593443
  if (process.platform !== "linux")
593084
593444
  return false;
593085
593445
  try {
593086
- const fs25 = await import("node:fs");
593087
- fs25.accessSync("/dev/kvm", fs25.constants.R_OK | fs25.constants.W_OK);
593446
+ const fs26 = await import("node:fs");
593447
+ fs26.accessSync("/dev/kvm", fs26.constants.R_OK | fs26.constants.W_OK);
593088
593448
  return true;
593089
593449
  } catch {
593090
593450
  return false;
@@ -593194,26 +593554,6 @@ function buildAcquireDeviceDeps(sdk, abi, log3) {
593194
593554
  };
593195
593555
  }
593196
593556
 
593197
- // dist/core/tests/mobilePlatform.js
593198
- function isMobileTargetPlatform(platform) {
593199
- if (platform === "android" || platform === "ios")
593200
- return platform;
593201
- return null;
593202
- }
593203
- function mobileContextSkipReason({ platform }) {
593204
- const roadmap = "docs/design/native-app-surfaces.md";
593205
- if (platform === "ios") {
593206
- return {
593207
- level: "warning",
593208
- reason: `Skipping context on 'ios': mobile browser testing on iOS lands in phase A5 of the native app roadmap (${roadmap}). Native iOS app tests run on capable macOS hosts.`
593209
- };
593210
- }
593211
- return {
593212
- level: "warning",
593213
- reason: `Skipping context on 'android': mobile browser testing on Android lands in phase A5 of the native app roadmap (${roadmap}). Native Android app tests (no browser steps) run today.`
593214
- };
593215
- }
593216
-
593217
593557
  // dist/core/tests/appSurface.js
593218
593558
  init_validate();
593219
593559
  function classifyAppIdentifier(app) {
@@ -593617,9 +593957,9 @@ function resolveAppSurfaceRef(surface, appSession) {
593617
593957
  function invalidateStaleAppiumManifest(home, driverPackage) {
593618
593958
  const cacheDir = import_node_path13.default.join(home, "node_modules", ".cache", "appium");
593619
593959
  try {
593620
- const manifest = import_node_fs14.default.readFileSync(import_node_path13.default.join(cacheDir, "extensions.yaml"), "utf8");
593960
+ const manifest = import_node_fs15.default.readFileSync(import_node_path13.default.join(cacheDir, "extensions.yaml"), "utf8");
593621
593961
  if (!manifest.includes(driverPackage)) {
593622
- import_node_fs14.default.rmSync(cacheDir, { recursive: true, force: true });
593962
+ import_node_fs15.default.rmSync(cacheDir, { recursive: true, force: true });
593623
593963
  }
593624
593964
  } catch {
593625
593965
  }
@@ -594041,7 +594381,87 @@ async function closeAppSurface({ entry, appSession }) {
594041
594381
  } catch {
594042
594382
  }
594043
594383
  }
594044
- async function teardownAppSession(appSession, killServer) {
594384
+ function defaultIsAlive(pid) {
594385
+ try {
594386
+ process.kill(pid, 0);
594387
+ return true;
594388
+ } catch (error) {
594389
+ return error?.code !== "ESRCH";
594390
+ }
594391
+ }
594392
+ function defaultForceKill(pid) {
594393
+ try {
594394
+ process.kill(pid, "SIGKILL");
594395
+ } catch {
594396
+ }
594397
+ }
594398
+ function queryWindowsProcessTable() {
594399
+ return new Promise((resolve, reject) => {
594400
+ const script = 'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId),$($_.ParentProcessId)" }';
594401
+ (0, import_node_child_process8.execFile)("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { windowsHide: true, timeout: 1e4, maxBuffer: 16 * 1024 * 1024 }, (error, stdout) => {
594402
+ if (error)
594403
+ return reject(error);
594404
+ const rows = [];
594405
+ for (const line of String(stdout).split(/\r?\n/)) {
594406
+ const m = line.trim().match(/^(\d+),(\d+)$/);
594407
+ if (m)
594408
+ rows.push({ pid: Number(m[1]), ppid: Number(m[2]) });
594409
+ }
594410
+ resolve(rows);
594411
+ });
594412
+ });
594413
+ }
594414
+ async function listWindowsDescendantPids(rootPid, runQuery) {
594415
+ const out = /* @__PURE__ */ new Set();
594416
+ let table;
594417
+ try {
594418
+ table = await runQuery();
594419
+ } catch {
594420
+ return out;
594421
+ }
594422
+ const childrenByParent = /* @__PURE__ */ new Map();
594423
+ for (const { pid, ppid } of table) {
594424
+ const siblings = childrenByParent.get(ppid);
594425
+ if (siblings)
594426
+ siblings.push(pid);
594427
+ else
594428
+ childrenByParent.set(ppid, [pid]);
594429
+ }
594430
+ const stack = [rootPid];
594431
+ while (stack.length) {
594432
+ const parent = stack.pop();
594433
+ for (const child of childrenByParent.get(parent) ?? []) {
594434
+ if (!out.has(child)) {
594435
+ out.add(child);
594436
+ stack.push(child);
594437
+ }
594438
+ }
594439
+ }
594440
+ return out;
594441
+ }
594442
+ async function snapshotAppServerDescendants(serverPid, tools = {}) {
594443
+ if (process.platform !== "win32" || !serverPid)
594444
+ return /* @__PURE__ */ new Set();
594445
+ const isAlive = tools.isAlive ?? defaultIsAlive;
594446
+ if (!isAlive(serverPid))
594447
+ return /* @__PURE__ */ new Set();
594448
+ if (tools.listDescendants)
594449
+ return tools.listDescendants(serverPid);
594450
+ return listWindowsDescendantPids(serverPid, tools.runQuery ?? queryWindowsProcessTable);
594451
+ }
594452
+ async function reapConsoleOrphans(pids, tools = {}) {
594453
+ const isAlive = tools.isAlive ?? defaultIsAlive;
594454
+ const forceKill = tools.forceKill ?? defaultForceKill;
594455
+ const reaped = [];
594456
+ for (const pid of pids) {
594457
+ if (isAlive(pid)) {
594458
+ forceKill(pid);
594459
+ reaped.push(pid);
594460
+ }
594461
+ }
594462
+ return reaped;
594463
+ }
594464
+ async function teardownAppSession(appSession, killServer, tools = {}) {
594045
594465
  if (!appSession)
594046
594466
  return;
594047
594467
  for (const entry of [...appSession.surfaces.values()]) {
@@ -594055,8 +594475,13 @@ async function teardownAppSession(appSession, killServer) {
594055
594475
  }
594056
594476
  appSession.deviceSessions?.clear();
594057
594477
  if (appSession.server) {
594058
- await killServer(appSession.server.process?.pid);
594478
+ const serverPid = appSession.server.process?.pid;
594479
+ const orphanSnapshot = await snapshotAppServerDescendants(serverPid, tools);
594480
+ await killServer(serverPid);
594059
594481
  appSession.server = void 0;
594482
+ if (orphanSnapshot.size) {
594483
+ await reapConsoleOrphans(orphanSnapshot, tools);
594484
+ }
594060
594485
  }
594061
594486
  }
594062
594487
 
@@ -594449,8 +594874,7 @@ init_loader();
594449
594874
  init_utils();
594450
594875
 
594451
594876
  // dist/core/tests/browserWait.js
594452
- async function waitForNetworkIdle(driver, idleTime, timeout) {
594453
- const startTime = Date.now();
594877
+ async function injectNetworkMonitor(driver) {
594454
594878
  await driver.execute(() => {
594455
594879
  if (!window.__docDetectiveNetworkMonitor) {
594456
594880
  const originalFetch = window.fetch;
@@ -594476,74 +594900,73 @@ async function waitForNetworkIdle(driver, idleTime, timeout) {
594476
594900
  };
594477
594901
  }
594478
594902
  });
594903
+ }
594904
+ async function cleanupNetworkMonitor(driver) {
594905
+ await driver.execute(() => {
594906
+ if (window.__docDetectiveNetworkMonitor) {
594907
+ if (window.__docDetectiveNetworkMonitor.originalFetch) {
594908
+ window.fetch = window.__docDetectiveNetworkMonitor.originalFetch;
594909
+ }
594910
+ if (window.__docDetectiveNetworkMonitor.originalXHROpen) {
594911
+ XMLHttpRequest.prototype.open = window.__docDetectiveNetworkMonitor.originalXHROpen;
594912
+ }
594913
+ delete window.__docDetectiveNetworkMonitor;
594914
+ }
594915
+ });
594916
+ }
594917
+ async function waitForNetworkIdle(driver, idleTime, timeout) {
594918
+ const startTime = Date.now();
594919
+ await injectNetworkMonitor(driver);
594479
594920
  await new Promise((resolve) => setTimeout(resolve, 100));
594480
594921
  const initialCheck = await driver.execute(() => {
594481
594922
  const monitor = window.__docDetectiveNetworkMonitor;
594923
+ if (!monitor)
594924
+ return null;
594482
594925
  const now = Date.now();
594483
594926
  return {
594484
594927
  idleFor: now - monitor.lastRequestTime,
594485
594928
  requestCount: monitor.requestCount
594486
594929
  };
594487
594930
  });
594488
- if (initialCheck.idleFor >= idleTime && initialCheck.requestCount === 0) {
594489
- await driver.execute(() => {
594490
- if (window.__docDetectiveNetworkMonitor) {
594491
- if (window.__docDetectiveNetworkMonitor.originalFetch) {
594492
- window.fetch = window.__docDetectiveNetworkMonitor.originalFetch;
594493
- }
594494
- if (window.__docDetectiveNetworkMonitor.originalXHROpen) {
594495
- XMLHttpRequest.prototype.open = window.__docDetectiveNetworkMonitor.originalXHROpen;
594496
- }
594497
- delete window.__docDetectiveNetworkMonitor;
594498
- }
594499
- });
594931
+ if (initialCheck && initialCheck.idleFor >= idleTime && initialCheck.requestCount === 0) {
594932
+ await cleanupNetworkMonitor(driver);
594500
594933
  return;
594501
594934
  }
594502
594935
  try {
594936
+ let needsInject = !initialCheck;
594503
594937
  while (true) {
594504
594938
  if (Date.now() - startTime > timeout) {
594505
- await driver.execute(() => {
594506
- if (window.__docDetectiveNetworkMonitor) {
594507
- if (window.__docDetectiveNetworkMonitor.originalFetch) {
594508
- window.fetch = window.__docDetectiveNetworkMonitor.originalFetch;
594509
- }
594510
- if (window.__docDetectiveNetworkMonitor.originalXHROpen) {
594511
- XMLHttpRequest.prototype.open = window.__docDetectiveNetworkMonitor.originalXHROpen;
594512
- }
594513
- delete window.__docDetectiveNetworkMonitor;
594514
- }
594515
- });
594516
594939
  throw new Error("Network idle timeout exceeded");
594517
594940
  }
594941
+ if (needsInject) {
594942
+ await injectNetworkMonitor(driver);
594943
+ needsInject = false;
594944
+ }
594518
594945
  const state = await driver.execute(() => {
594519
594946
  const monitor = window.__docDetectiveNetworkMonitor;
594947
+ if (!monitor)
594948
+ return null;
594520
594949
  const now = Date.now();
594521
594950
  return {
594522
594951
  idleFor: now - monitor.lastRequestTime,
594523
594952
  elapsedTotal: now - monitor.startTime
594524
594953
  };
594525
594954
  });
594955
+ if (state === null || state === void 0) {
594956
+ needsInject = true;
594957
+ await new Promise((resolve) => setTimeout(resolve, 200));
594958
+ continue;
594959
+ }
594526
594960
  if (state.idleFor >= idleTime) {
594527
594961
  break;
594528
594962
  }
594529
594963
  await new Promise((resolve) => setTimeout(resolve, 200));
594530
594964
  }
594531
594965
  } finally {
594532
- await driver.execute(() => {
594533
- if (window.__docDetectiveNetworkMonitor) {
594534
- if (window.__docDetectiveNetworkMonitor.originalFetch) {
594535
- window.fetch = window.__docDetectiveNetworkMonitor.originalFetch;
594536
- }
594537
- if (window.__docDetectiveNetworkMonitor.originalXHROpen) {
594538
- XMLHttpRequest.prototype.open = window.__docDetectiveNetworkMonitor.originalXHROpen;
594539
- }
594540
- delete window.__docDetectiveNetworkMonitor;
594541
- }
594542
- });
594966
+ await cleanupNetworkMonitor(driver);
594543
594967
  }
594544
594968
  }
594545
- async function waitForDOMStable(driver, idleTime, timeout) {
594546
- const startTime = Date.now();
594969
+ async function injectDOMMonitor(driver) {
594547
594970
  await driver.execute(() => {
594548
594971
  if (!window.__docDetectiveDOMMonitor) {
594549
594972
  window.__docDetectiveDOMMonitor = {
@@ -594567,37 +594990,47 @@ async function waitForDOMStable(driver, idleTime, timeout) {
594567
594990
  window.__docDetectiveDOMMonitor.observer = observer;
594568
594991
  }
594569
594992
  });
594993
+ }
594994
+ async function cleanupDOMMonitor(driver) {
594995
+ await driver.execute(() => {
594996
+ if (window.__docDetectiveDOMMonitor?.observer) {
594997
+ window.__docDetectiveDOMMonitor.observer.disconnect();
594998
+ }
594999
+ delete window.__docDetectiveDOMMonitor;
595000
+ });
595001
+ }
595002
+ async function waitForDOMStable(driver, idleTime, timeout) {
595003
+ const startTime = Date.now();
595004
+ await injectDOMMonitor(driver);
594570
595005
  await new Promise((resolve) => setTimeout(resolve, 100));
594571
595006
  const initialCheck = await driver.execute(() => {
594572
595007
  const monitor = window.__docDetectiveDOMMonitor;
595008
+ if (!monitor)
595009
+ return null;
594573
595010
  const now = Date.now();
594574
595011
  return {
594575
595012
  idleFor: now - monitor.lastMutationTime,
594576
595013
  mutationCount: monitor.mutationCount
594577
595014
  };
594578
595015
  });
594579
- if (initialCheck.idleFor >= idleTime && initialCheck.mutationCount === 0) {
594580
- await driver.execute(() => {
594581
- if (window.__docDetectiveDOMMonitor?.observer) {
594582
- window.__docDetectiveDOMMonitor.observer.disconnect();
594583
- delete window.__docDetectiveDOMMonitor;
594584
- }
594585
- });
595016
+ if (initialCheck && initialCheck.idleFor >= idleTime && initialCheck.mutationCount === 0) {
595017
+ await cleanupDOMMonitor(driver);
594586
595018
  return;
594587
595019
  }
594588
595020
  try {
595021
+ let needsInject = !initialCheck;
594589
595022
  while (true) {
594590
595023
  if (Date.now() - startTime > timeout) {
594591
- await driver.execute(() => {
594592
- if (window.__docDetectiveDOMMonitor?.observer) {
594593
- window.__docDetectiveDOMMonitor.observer.disconnect();
594594
- delete window.__docDetectiveDOMMonitor;
594595
- }
594596
- });
594597
595024
  throw new Error("DOM stability timeout exceeded");
594598
595025
  }
595026
+ if (needsInject) {
595027
+ await injectDOMMonitor(driver);
595028
+ needsInject = false;
595029
+ }
594599
595030
  const state = await driver.execute(() => {
594600
595031
  const monitor = window.__docDetectiveDOMMonitor;
595032
+ if (!monitor)
595033
+ return null;
594601
595034
  const now = Date.now();
594602
595035
  return {
594603
595036
  idleFor: now - monitor.lastMutationTime,
@@ -594605,28 +595038,23 @@ async function waitForDOMStable(driver, idleTime, timeout) {
594605
595038
  mutationCount: monitor.mutationCount
594606
595039
  };
594607
595040
  });
595041
+ if (state === null || state === void 0) {
595042
+ needsInject = true;
595043
+ await new Promise((resolve) => setTimeout(resolve, 200));
595044
+ continue;
595045
+ }
594608
595046
  if (state.idleFor >= idleTime) {
594609
- await driver.execute(() => {
594610
- if (window.__docDetectiveDOMMonitor?.observer) {
594611
- window.__docDetectiveDOMMonitor.observer.disconnect();
594612
- delete window.__docDetectiveDOMMonitor;
594613
- }
594614
- });
594615
595047
  break;
594616
595048
  }
594617
595049
  await new Promise((resolve) => setTimeout(resolve, 200));
594618
595050
  }
594619
595051
  } catch (error) {
594620
- await driver.execute(() => {
594621
- if (window.__docDetectiveDOMMonitor?.observer) {
594622
- window.__docDetectiveDOMMonitor.observer.disconnect();
594623
- delete window.__docDetectiveDOMMonitor;
594624
- }
594625
- });
595052
+ await cleanupDOMMonitor(driver);
594626
595053
  throw new Error(`DOM stability check failed: ${error.message}`, {
594627
595054
  cause: error
594628
595055
  });
594629
595056
  }
595057
+ await cleanupDOMMonitor(driver);
594630
595058
  }
594631
595059
 
594632
595060
  // dist/core/expressions.js
@@ -594701,11 +595129,11 @@ function hasUnresolvedMetaReference(expression, context) {
594701
595129
  }
594702
595130
  return false;
594703
595131
  }
594704
- function getMetaValue(path24, context) {
595132
+ function getMetaValue(path25, context) {
594705
595133
  if (!context) {
594706
595134
  return void 0;
594707
595135
  }
594708
- const [basePath, jsonPointer] = path24.split("#");
595136
+ const [basePath, jsonPointer] = path25.split("#");
594709
595137
  const resolvedPath = resolvePathTemplateVariables(basePath, context);
594710
595138
  let value = getNestedProperty(context, resolvedPath);
594711
595139
  if (jsonPointer && value) {
@@ -594722,9 +595150,9 @@ function getMetaValue(path24, context) {
594722
595150
  }
594723
595151
  return value;
594724
595152
  }
594725
- function resolvePathTemplateVariables(path24, context) {
595153
+ function resolvePathTemplateVariables(path25, context) {
594726
595154
  const templateRegex = /\{\{(\w+)\}\}/g;
594727
- return path24.replace(templateRegex, (match, varName) => {
595155
+ return path25.replace(templateRegex, (match, varName) => {
594728
595156
  if (context && context.id && varName === "id") {
594729
595157
  return context.id;
594730
595158
  }
@@ -594761,10 +595189,10 @@ async function resolveEmbeddedExpressions(str, context) {
594761
595189
  parts.push(str.slice(lastIdx));
594762
595190
  return parts.join("");
594763
595191
  }
594764
- function getNestedProperty(obj, path24) {
594765
- if (!obj || !path24)
595192
+ function getNestedProperty(obj, path25) {
595193
+ if (!obj || !path25)
594766
595194
  return void 0;
594767
- const parts = path24.split(".");
595195
+ const parts = path25.split(".");
594768
595196
  let current = obj;
594769
595197
  for (const part of parts) {
594770
595198
  if (current === null || current === void 0)
@@ -595757,7 +596185,7 @@ async function findElement({ config, step, driver, click, appSession }) {
595757
596185
  await finalizeFound({ result });
595758
596186
  if (click) {
595759
596187
  try {
595760
- await element2.click({ button: "left" });
596188
+ await element2.click();
595761
596189
  result.description += " Clicked element.";
595762
596190
  } catch (error2) {
595763
596191
  result.status = "FAIL";
@@ -595829,9 +596257,12 @@ async function findElement({ config, step, driver, click, appSession }) {
595829
596257
  }
595830
596258
  if (step.find.click || click) {
595831
596259
  try {
595832
- await element.click({
595833
- button: step.find.click?.button || "left"
595834
- });
596260
+ const button = step.find.click?.button || "left";
596261
+ if (button === "left") {
596262
+ await element.click();
596263
+ } else {
596264
+ await element.click({ button });
596265
+ }
595835
596266
  result.description += " Clicked element.";
595836
596267
  } catch (error2) {
595837
596268
  result.status = "FAIL";
@@ -596124,7 +596555,7 @@ async function goTo({ config, step, driver }) {
596124
596555
  init_validate();
596125
596556
  init_utils();
596126
596557
  var import_tree_kill = __toESM(require("tree-kill"), 1);
596127
- var import_node_fs15 = __toESM(require("node:fs"), 1);
596558
+ var import_node_fs16 = __toESM(require("node:fs"), 1);
596128
596559
  var import_node_path14 = __toESM(require("node:path"), 1);
596129
596560
  async function runShell({ config, step, driver, processRegistry }) {
596130
596561
  const result = {
@@ -596273,18 +596704,18 @@ async function runShell({ config, step, driver, processRegistry }) {
596273
596704
  }
596274
596705
  if (step.runShell.path) {
596275
596706
  const dir = import_node_path14.default.dirname(step.runShell.path);
596276
- if (!import_node_fs15.default.existsSync(dir)) {
596277
- import_node_fs15.default.mkdirSync(dir, { recursive: true });
596707
+ if (!import_node_fs16.default.existsSync(dir)) {
596708
+ import_node_fs16.default.mkdirSync(dir, { recursive: true });
596278
596709
  }
596279
596710
  let filePath = step.runShell.path;
596280
596711
  log(config, "debug", `Saving stdio to file: ${filePath}`);
596281
- if (!import_node_fs15.default.existsSync(filePath)) {
596282
- import_node_fs15.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596712
+ if (!import_node_fs16.default.existsSync(filePath)) {
596713
+ import_node_fs16.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596283
596714
  } else {
596284
596715
  if (step.runShell.overwrite == "false") {
596285
596716
  descriptions.push(`Didn't save output. File already exists.`);
596286
596717
  }
596287
- const existingFile = import_node_fs15.default.readFileSync(filePath, "utf8");
596718
+ const existingFile = import_node_fs16.default.readFileSync(filePath, "utf8");
596288
596719
  const fractionalDiff = calculateFractionalDifference(existingFile, result.outputs.stdio.stdout);
596289
596720
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
596290
596721
  result.outputs.variation = fractionalDiff;
@@ -596294,13 +596725,13 @@ async function runShell({ config, step, driver, processRegistry }) {
596294
596725
  });
596295
596726
  if (fractionalDiff > step.runShell.maxVariation) {
596296
596727
  if (step.runShell.overwrite == "aboveVariation") {
596297
- import_node_fs15.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596728
+ import_node_fs16.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596298
596729
  descriptions.push(`Saved output to file.`);
596299
596730
  }
596300
596731
  descriptions.push(`The difference between the existing output and the new output (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.runShell.maxVariation}).`);
596301
596732
  } else {
596302
596733
  if (step.runShell.overwrite == "true") {
596303
- import_node_fs15.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596734
+ import_node_fs16.default.writeFileSync(filePath, result.outputs.stdio.stdout);
596304
596735
  descriptions.push(`Saved output to file.`);
596305
596736
  }
596306
596737
  descriptions.push(`Saved-file variation (${fractionalDiff.toFixed(2)}) is within the max accepted variation (${step.runShell.maxVariation}).`);
@@ -596499,7 +596930,7 @@ async function checkLink({ config, step }) {
596499
596930
  init_validate();
596500
596931
  init_utils();
596501
596932
  var import_node_path15 = __toESM(require("node:path"), 1);
596502
- var import_node_fs16 = __toESM(require("node:fs"), 1);
596933
+ var import_node_fs17 = __toESM(require("node:fs"), 1);
596503
596934
  init_loader();
596504
596935
  var _pngjs = null;
596505
596936
  var _sharp = null;
@@ -596678,8 +597109,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596678
597109
  const rawBase = import_node_path15.default.basename(urlPathname.split("?")[0].split("#")[0].replace(/\\/g, "/"));
596679
597110
  const safeBase = sanitizeFilesystemName(rawBase, `${step.stepId}.png`);
596680
597111
  dir = import_node_path15.default.join(process.cwd(), "doc-detective-runs", getOrInitRunTimestamp(config));
596681
- if (!import_node_fs16.default.existsSync(dir)) {
596682
- import_node_fs16.default.mkdirSync(dir, { recursive: true });
597112
+ if (!import_node_fs17.default.existsSync(dir)) {
597113
+ import_node_fs17.default.mkdirSync(dir, { recursive: true });
596683
597114
  }
596684
597115
  const captureId = `${step.stepId || "screenshot"}_${Date.now()}`;
596685
597116
  filePath = import_node_path15.default.join(dir, `${captureId}_${safeBase}`);
@@ -596695,10 +597126,10 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596695
597126
  }
596696
597127
  } else {
596697
597128
  dir = import_node_path15.default.dirname(step.screenshot.path);
596698
- if (!import_node_fs16.default.existsSync(dir)) {
596699
- import_node_fs16.default.mkdirSync(dir, { recursive: true });
597129
+ if (!import_node_fs17.default.existsSync(dir)) {
597130
+ import_node_fs17.default.mkdirSync(dir, { recursive: true });
596700
597131
  }
596701
- if (import_node_fs16.default.existsSync(filePath)) {
597132
+ if (import_node_fs17.default.existsSync(filePath)) {
596702
597133
  if (step.screenshot.overwrite == "false") {
596703
597134
  result.status = "SKIPPED";
596704
597135
  result.description = `File already exists: ${filePath}`;
@@ -596804,8 +597235,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596804
597235
  } catch (error) {
596805
597236
  result.status = "FAIL";
596806
597237
  result.description = `Couldn't save screenshot. ${error}`;
596807
- if (existFilePath && filePath !== existFilePath && import_node_fs16.default.existsSync(filePath)) {
596808
- import_node_fs16.default.unlinkSync(filePath);
597238
+ if (existFilePath && filePath !== existFilePath && import_node_fs17.default.existsSync(filePath)) {
597239
+ import_node_fs17.default.unlinkSync(filePath);
596809
597240
  }
596810
597241
  return result;
596811
597242
  } finally {
@@ -596868,12 +597299,12 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596868
597299
  width: rect.width,
596869
597300
  height: rect.height
596870
597301
  }).toFile(croppedPath);
596871
- import_node_fs16.default.renameSync(croppedPath, filePath);
597302
+ import_node_fs17.default.renameSync(croppedPath, filePath);
596872
597303
  } catch (error) {
596873
597304
  result.status = "FAIL";
596874
597305
  result.description = `Couldn't crop image. ${error}`;
596875
- if (existFilePath && filePath !== existFilePath && import_node_fs16.default.existsSync(filePath)) {
596876
- import_node_fs16.default.unlinkSync(filePath);
597306
+ if (existFilePath && filePath !== existFilePath && import_node_fs17.default.existsSync(filePath)) {
597307
+ import_node_fs17.default.unlinkSync(filePath);
596877
597308
  }
596878
597309
  return result;
596879
597310
  }
@@ -596881,7 +597312,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596881
597312
  if (existFilePath) {
596882
597313
  if (step.screenshot.overwrite == "true" && !isUrlPath) {
596883
597314
  result.description += ` Overwrote existing file.`;
596884
- import_node_fs16.default.renameSync(filePath, existFilePath);
597315
+ import_node_fs17.default.renameSync(filePath, existFilePath);
596885
597316
  result.outputs.screenshotPath = existFilePath;
596886
597317
  result.outputs.changed = true;
596887
597318
  if (step.screenshot.sourceIntegration) {
@@ -596894,13 +597325,13 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596894
597325
  let img1;
596895
597326
  let img2;
596896
597327
  try {
596897
- img1 = PNG.sync.read(import_node_fs16.default.readFileSync(existFilePath));
596898
- img2 = PNG.sync.read(import_node_fs16.default.readFileSync(filePath));
597328
+ img1 = PNG.sync.read(import_node_fs17.default.readFileSync(existFilePath));
597329
+ img2 = PNG.sync.read(import_node_fs17.default.readFileSync(filePath));
596899
597330
  } catch (error) {
596900
597331
  result.status = "FAIL";
596901
597332
  result.description = isUrlPath ? `Couldn't decode PNG for comparison. The URL reference (${redactedUrl}) may not be a valid PNG. ${error}` : `Couldn't decode PNG for comparison. ${error}`;
596902
- if (!isUrlPath && filePath !== existFilePath && import_node_fs16.default.existsSync(filePath)) {
596903
- import_node_fs16.default.unlinkSync(filePath);
597333
+ if (!isUrlPath && filePath !== existFilePath && import_node_fs17.default.existsSync(filePath)) {
597334
+ import_node_fs17.default.unlinkSync(filePath);
596904
597335
  }
596905
597336
  return result;
596906
597337
  }
@@ -596912,8 +597343,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596912
597343
  });
596913
597344
  if (!aspectRatioMatch) {
596914
597345
  result.description = `Couldn't compare images. Images have different aspect ratios.`;
596915
- if (!isUrlPath && filePath !== existFilePath && import_node_fs16.default.existsSync(filePath)) {
596916
- import_node_fs16.default.unlinkSync(filePath);
597346
+ if (!isUrlPath && filePath !== existFilePath && import_node_fs17.default.existsSync(filePath)) {
597347
+ import_node_fs17.default.unlinkSync(filePath);
596917
597348
  }
596918
597349
  return await evaluateApplicable();
596919
597350
  }
@@ -596940,8 +597371,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596940
597371
  } catch (error) {
596941
597372
  result.status = "FAIL";
596942
597373
  result.description = `Couldn't load screenshot comparison dependency (pixelmatch). ${error?.message ?? error}`;
596943
- if (!isUrlPath && filePath !== existFilePath && import_node_fs16.default.existsSync(filePath)) {
596944
- import_node_fs16.default.unlinkSync(filePath);
597374
+ if (!isUrlPath && filePath !== existFilePath && import_node_fs17.default.existsSync(filePath)) {
597375
+ import_node_fs17.default.unlinkSync(filePath);
596945
597376
  }
596946
597377
  return result;
596947
597378
  }
@@ -596959,7 +597390,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596959
597390
  });
596960
597391
  if (fractionalDiff > step.screenshot.maxVariation) {
596961
597392
  if (step.screenshot.overwrite == "aboveVariation" && !isUrlPath) {
596962
- import_node_fs16.default.renameSync(filePath, existFilePath);
597393
+ import_node_fs17.default.renameSync(filePath, existFilePath);
596963
597394
  }
596964
597395
  result.description += ` The difference between the existing screenshot and new screenshot (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.screenshot.maxVariation}).`;
596965
597396
  if (isUrlPath) {
@@ -596984,7 +597415,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
596984
597415
  result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
596985
597416
  }
596986
597417
  if (step.screenshot.overwrite != "true") {
596987
- import_node_fs16.default.unlinkSync(filePath);
597418
+ import_node_fs17.default.unlinkSync(filePath);
596988
597419
  }
596989
597420
  }
596990
597421
  }
@@ -597006,7 +597437,7 @@ init_utils();
597006
597437
  var import_node_child_process9 = require("node:child_process");
597007
597438
  var import_node_crypto6 = require("node:crypto");
597008
597439
  var import_node_path16 = __toESM(require("node:path"), 1);
597009
- var import_node_fs17 = __toESM(require("node:fs"), 1);
597440
+ var import_node_fs18 = __toESM(require("node:fs"), 1);
597010
597441
  var import_node_os8 = __toESM(require("node:os"), 1);
597011
597442
  async function startRecording({ config, context, step, driver, recordingHost }) {
597012
597443
  let result = {
@@ -597053,10 +597484,10 @@ async function startRecording({ config, context, step, driver, recordingHost })
597053
597484
  let filePath = step.record.path;
597054
597485
  const baseName = import_node_path16.default.basename(filePath, import_node_path16.default.extname(filePath));
597055
597486
  const dir = import_node_path16.default.dirname(step.record.path);
597056
- if (!import_node_fs17.default.existsSync(dir)) {
597057
- import_node_fs17.default.mkdirSync(dir, { recursive: true });
597487
+ if (!import_node_fs18.default.existsSync(dir)) {
597488
+ import_node_fs18.default.mkdirSync(dir, { recursive: true });
597058
597489
  }
597059
- if (import_node_fs17.default.existsSync(filePath) && step.record.overwrite == "false") {
597490
+ if (import_node_fs18.default.existsSync(filePath) && step.record.overwrite == "false") {
597060
597491
  result.status = "SKIPPED";
597061
597492
  result.description = `File already exists: ${filePath}`;
597062
597493
  return result;
@@ -597091,13 +597522,13 @@ async function startRecording({ config, context, step, driver, recordingHost })
597091
597522
  }
597092
597523
  const captureTitle = browserCaptureTitle(context.contextId);
597093
597524
  const downloadDir = browserDownloadDir(context.contextId);
597094
- if (!import_node_fs17.default.existsSync(downloadDir)) {
597095
- import_node_fs17.default.mkdirSync(downloadDir, { recursive: true });
597525
+ if (!import_node_fs18.default.existsSync(downloadDir)) {
597526
+ import_node_fs18.default.mkdirSync(downloadDir, { recursive: true });
597096
597527
  }
597097
597528
  const downloadPath = import_node_path16.default.join(downloadDir, `${baseName}.webm`);
597098
- if (import_node_fs17.default.existsSync(downloadPath)) {
597529
+ if (import_node_fs18.default.existsSync(downloadPath)) {
597099
597530
  try {
597100
- import_node_fs17.default.unlinkSync(downloadPath);
597531
+ import_node_fs18.default.unlinkSync(downloadPath);
597101
597532
  } catch {
597102
597533
  }
597103
597534
  }
@@ -597228,8 +597659,8 @@ async function startRecording({ config, context, step, driver, recordingHost })
597228
597659
  }
597229
597660
  }
597230
597661
  const tempDir = import_node_path16.default.join(import_node_os8.default.tmpdir(), "doc-detective", "recordings");
597231
- if (!import_node_fs17.default.existsSync(tempDir))
597232
- import_node_fs17.default.mkdirSync(tempDir, { recursive: true });
597662
+ if (!import_node_fs18.default.existsSync(tempDir))
597663
+ import_node_fs18.default.mkdirSync(tempDir, { recursive: true });
597233
597664
  const tempPath = import_node_path16.default.join(tempDir, `${safeContextId(context.contextId)}-${baseName}-${(0, import_node_crypto6.randomUUID)().slice(0, 8)}.mkv`);
597234
597665
  let ffmpegPath;
597235
597666
  try {
@@ -597290,7 +597721,7 @@ init_validate();
597290
597721
  init_utils();
597291
597722
  var import_node_child_process10 = require("node:child_process");
597292
597723
  var import_node_path17 = __toESM(require("node:path"), 1);
597293
- var import_node_fs18 = __toESM(require("node:fs"), 1);
597724
+ var import_node_fs19 = __toESM(require("node:fs"), 1);
597294
597725
  async function stopRecording({ config, step, driver }) {
597295
597726
  let result = {
597296
597727
  status: "PASS",
@@ -597467,7 +597898,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
597467
597898
  if (code === 0) {
597468
597899
  if (deleteSource && sourcePath !== targetPath) {
597469
597900
  try {
597470
- import_node_fs18.default.unlinkSync(sourcePath);
597901
+ import_node_fs19.default.unlinkSync(sourcePath);
597471
597902
  } catch {
597472
597903
  }
597473
597904
  }
@@ -597486,7 +597917,7 @@ async function waitForStableFile(filePath, maxSeconds) {
597486
597917
  for (let i = 0; i < deadline; i++) {
597487
597918
  let size = -1;
597488
597919
  try {
597489
- size = import_node_fs18.default.statSync(filePath).size;
597920
+ size = import_node_fs19.default.statSync(filePath).size;
597490
597921
  } catch {
597491
597922
  size = -1;
597492
597923
  }
@@ -597527,7 +597958,7 @@ async function loadVariables({ step }) {
597527
597958
  init_validate();
597528
597959
  init_utils();
597529
597960
  var import_node_path18 = __toESM(require("node:path"), 1);
597530
- var import_node_fs19 = __toESM(require("node:fs"), 1);
597961
+ var import_node_fs20 = __toESM(require("node:fs"), 1);
597531
597962
  async function saveCookie({ config, step, driver }) {
597532
597963
  let result = {
597533
597964
  status: "PASS",
@@ -597594,14 +598025,14 @@ async function saveCookie({ config, step, driver }) {
597594
598025
  if (filePath) {
597595
598026
  const outputDirectory = directory || config.output || process.cwd();
597596
598027
  const fullPath = import_node_path18.default.resolve(outputDirectory, filePath);
597597
- if (import_node_fs19.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
598028
+ if (import_node_fs20.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
597598
598029
  result.status = "FAIL";
597599
598030
  result.description = `File '${fullPath}' already exists and overwrite is not enabled.`;
597600
598031
  return result;
597601
598032
  }
597602
598033
  const dir = import_node_path18.default.dirname(fullPath);
597603
- if (!import_node_fs19.default.existsSync(dir)) {
597604
- import_node_fs19.default.mkdirSync(dir, { recursive: true });
598034
+ if (!import_node_fs20.default.existsSync(dir)) {
598035
+ import_node_fs20.default.mkdirSync(dir, { recursive: true });
597605
598036
  }
597606
598037
  if (targetCookie) {
597607
598038
  const netscapeCookie = formatCookieForNetscape(targetCookie);
@@ -597609,11 +598040,11 @@ async function saveCookie({ config, step, driver }) {
597609
598040
  # This is a cookie file saved by Doc Detective
597610
598041
  ${netscapeCookie}
597611
598042
  `;
597612
- import_node_fs19.default.writeFileSync(fullPath, content, "utf8");
598043
+ import_node_fs20.default.writeFileSync(fullPath, content, "utf8");
597613
598044
  result.description = `Saved cookie '${cookieName}' to '${fullPath}'.`;
597614
598045
  log(config, "debug", `Saved cookie '${cookieName}' to file: ${fullPath}`);
597615
598046
  } else {
597616
- import_node_fs19.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
598047
+ import_node_fs20.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
597617
598048
  result.description = `Created empty cookie file at '${fullPath}'.`;
597618
598049
  log(config, "debug", `Created empty cookie file: ${fullPath}`);
597619
598050
  }
@@ -597641,7 +598072,7 @@ function formatCookieForNetscape(cookie) {
597641
598072
  init_validate();
597642
598073
  init_utils();
597643
598074
  var import_node_path19 = __toESM(require("node:path"), 1);
597644
- var import_node_fs20 = __toESM(require("node:fs"), 1);
598075
+ var import_node_fs21 = __toESM(require("node:fs"), 1);
597645
598076
  async function loadCookie({ config, step, driver }) {
597646
598077
  let result = {
597647
598078
  status: "PASS",
@@ -597697,13 +598128,13 @@ async function loadCookie({ config, step, driver }) {
597697
598128
  } else if (filePath) {
597698
598129
  const inputDirectory = directory || config.output || process.cwd();
597699
598130
  const fullPath = import_node_path19.default.resolve(inputDirectory, filePath);
597700
- if (!import_node_fs20.default.existsSync(fullPath)) {
598131
+ if (!import_node_fs21.default.existsSync(fullPath)) {
597701
598132
  result.status = "FAIL";
597702
598133
  result.description = `Cookie file '${fullPath}' not found`;
597703
598134
  return result;
597704
598135
  }
597705
598136
  try {
597706
- const fileContent = import_node_fs20.default.readFileSync(fullPath, "utf8");
598137
+ const fileContent = import_node_fs21.default.readFileSync(fullPath, "utf8");
597707
598138
  const cookies = parseNetscapeCookieFile(fileContent);
597708
598139
  if (cookies.length === 0) {
597709
598140
  result.status = "FAIL";
@@ -597869,7 +598300,7 @@ function isDomainCompatible(currentDomain, cookieDomain) {
597869
598300
  // dist/core/tests/httpRequest.js
597870
598301
  init_validate();
597871
598302
  var import_axios5 = __toESM(require("axios"), 1);
597872
- var import_node_fs21 = __toESM(require("node:fs"), 1);
598303
+ var import_node_fs22 = __toESM(require("node:fs"), 1);
597873
598304
  var import_node_path20 = __toESM(require("node:path"), 1);
597874
598305
  var import_ajv2 = __toESM(require("ajv"), 1);
597875
598306
  init_openapi();
@@ -597897,9 +598328,9 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
597897
598328
  delete step.httpRequest.openApi.definition;
597898
598329
  } else if (openApiDefinitions.length > 0) {
597899
598330
  for (const openApiConfig of openApiDefinitions) {
597900
- for (const path24 in openApiConfig.definition.paths) {
597901
- for (const method in openApiConfig.definition.paths[path24]) {
597902
- if (openApiConfig.definition.paths[path24][method].operationId === step.httpRequest.openApi.operationId) {
598331
+ for (const path25 in openApiConfig.definition.paths) {
598332
+ for (const method in openApiConfig.definition.paths[path25]) {
598333
+ if (openApiConfig.definition.paths[path25][method].operationId === step.httpRequest.openApi.operationId) {
597903
598334
  openApiDefinition = openApiConfig.definition;
597904
598335
  step.httpRequest.openApi = {
597905
598336
  ...openApiConfig,
@@ -598207,19 +598638,19 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
598207
598638
  }
598208
598639
  if (step.httpRequest.path) {
598209
598640
  const dir = import_node_path20.default.dirname(step.httpRequest.path);
598210
- if (!import_node_fs21.default.existsSync(dir)) {
598211
- import_node_fs21.default.mkdirSync(dir, { recursive: true });
598641
+ if (!import_node_fs22.default.existsSync(dir)) {
598642
+ import_node_fs22.default.mkdirSync(dir, { recursive: true });
598212
598643
  }
598213
598644
  let filePath = step.httpRequest.path;
598214
598645
  log(config, "debug", `Saving output to file: ${filePath}`);
598215
- if (!import_node_fs21.default.existsSync(filePath)) {
598216
- await import_node_fs21.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
598646
+ if (!import_node_fs22.default.existsSync(filePath)) {
598647
+ await import_node_fs22.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
598217
598648
  descriptions.push(`Saved output to file.`);
598218
598649
  } else {
598219
598650
  if (step.httpRequest.overwrite == "false") {
598220
598651
  descriptions.push(`Didn't save output. File already exists.`);
598221
598652
  }
598222
- const existingFile = import_node_fs21.default.readFileSync(filePath, "utf8");
598653
+ const existingFile = import_node_fs22.default.readFileSync(filePath, "utf8");
598223
598654
  const fractionalDiff = calculateFractionalDifference(existingFile, JSON.stringify(response.data, null, 2));
598224
598655
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
598225
598656
  result.outputs.variation = fractionalDiff;
@@ -598229,13 +598660,13 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
598229
598660
  });
598230
598661
  if (fractionalDiff > step.httpRequest.maxVariation) {
598231
598662
  if (step.httpRequest.overwrite == "aboveVariation") {
598232
- await import_node_fs21.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
598663
+ await import_node_fs22.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
598233
598664
  descriptions.push(`Saved response to file.`);
598234
598665
  }
598235
598666
  descriptions.push(`The difference between the existing saved response and the new response (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.httpRequest.maxVariation}).`);
598236
598667
  } else {
598237
598668
  if (step.httpRequest.overwrite == "true") {
598238
- import_node_fs21.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
598669
+ import_node_fs22.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
598239
598670
  descriptions.push(`Saved response to file.`);
598240
598671
  }
598241
598672
  }
@@ -598248,8 +598679,8 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
598248
598679
  result.description = descriptions.join(" ").trim();
598249
598680
  return result;
598250
598681
  }
598251
- function fieldExistsAtPath(obj, path24) {
598252
- const segments = path24.match(/[^.[\]]+/g);
598682
+ function fieldExistsAtPath(obj, path25) {
598683
+ const segments = path25.match(/[^.[\]]+/g);
598253
598684
  if (!segments) {
598254
598685
  return false;
598255
598686
  }
@@ -598437,7 +598868,7 @@ async function clickElement({ config, step, driver, appSession }) {
598437
598868
  // dist/core/tests/runCode.js
598438
598869
  init_validate();
598439
598870
  init_utils();
598440
- var import_node_fs22 = __toESM(require("node:fs"), 1);
598871
+ var import_node_fs23 = __toESM(require("node:fs"), 1);
598441
598872
  var import_node_path21 = __toESM(require("node:path"), 1);
598442
598873
  var import_node_os9 = __toESM(require("node:os"), 1);
598443
598874
  function createTempScript(code, language) {
@@ -598465,7 +598896,7 @@ function createTempScript(code, language) {
598465
598896
  const tmpDir = import_node_os9.default.tmpdir();
598466
598897
  const tmpFile = import_node_path21.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
598467
598898
  try {
598468
- import_node_fs22.default.writeFileSync(tmpFile, code);
598899
+ import_node_fs23.default.writeFileSync(tmpFile, code);
598469
598900
  } catch (error) {
598470
598901
  throw new Error(`Failed to create temporary script: ${error.message}`);
598471
598902
  }
@@ -598575,7 +599006,7 @@ async function runCode({ config, step, driver, processRegistry }) {
598575
599006
  } finally {
598576
599007
  if (!deferTempCleanup) {
598577
599008
  try {
598578
- import_node_fs22.default.unlinkSync(scriptPath);
599009
+ import_node_fs23.default.unlinkSync(scriptPath);
598579
599010
  log(config, "debug", `Removed temporary script: ${scriptPath}`);
598580
599011
  } catch (error) {
598581
599012
  log(config, "warning", `Failed to remove temporary script: ${scriptPath}`);
@@ -598589,7 +599020,7 @@ async function runCode({ config, step, driver, processRegistry }) {
598589
599020
  init_validate();
598590
599021
  init_utils();
598591
599022
  var import_tree_kill2 = __toESM(require("tree-kill"), 1);
598592
- var import_node_fs23 = __toESM(require("node:fs"), 1);
599023
+ var import_node_fs24 = __toESM(require("node:fs"), 1);
598593
599024
  async function closeSurface({ config, step, driver, processRegistry, appSession }) {
598594
599025
  const result = {
598595
599026
  status: "PASS",
@@ -598724,7 +599155,7 @@ async function closeSurface({ config, step, driver, processRegistry, appSession
598724
599155
  }
598725
599156
  if (entry.tempPath) {
598726
599157
  try {
598727
- import_node_fs23.default.unlinkSync(entry.tempPath);
599158
+ import_node_fs24.default.unlinkSync(entry.tempPath);
598728
599159
  } catch {
598729
599160
  }
598730
599161
  }
@@ -598747,6 +599178,106 @@ async function closeSurface({ config, step, driver, processRegistry, appSession
598747
599178
  return result;
598748
599179
  }
598749
599180
 
599181
+ // dist/core/tests/mobileBrowser.js
599182
+ var import_node_path22 = __toESM(require("node:path"), 1);
599183
+ var SUPPORTED_MOBILE_BROWSER = {
599184
+ android: "chrome",
599185
+ ios: "safari"
599186
+ };
599187
+ var MOBILE_CHROMEDRIVER_DIR = "chromedriver-mobile";
599188
+ function defaultMobileBrowserName(platform) {
599189
+ return SUPPORTED_MOBILE_BROWSER[platform];
599190
+ }
599191
+ function mobileBrowserSupport({ platform, browserName }) {
599192
+ const supported = SUPPORTED_MOBILE_BROWSER[platform];
599193
+ if (browserName === supported)
599194
+ return { supported: true };
599195
+ return {
599196
+ supported: false,
599197
+ reason: `Skipping context: '${browserName}' isn't available on ${platform}. '${supported}' is the supported ${platform} browser \u2014 set browsers: "${supported}" (or omit browsers) on the ${platform} runOn entry.`
599198
+ };
599199
+ }
599200
+ function mobileBrowserConfigError(browser) {
599201
+ if (!browser)
599202
+ return null;
599203
+ if (browser.headless === false) {
599204
+ return "browser headless is meaningless for a device browser \u2014 the device owns its display. Set headless on the device descriptor (device.headless) instead.";
599205
+ }
599206
+ const authoredDimensions = (value) => value && typeof value === "object" && Object.keys(value).length > 0;
599207
+ if (authoredDimensions(browser.window)) {
599208
+ return "browser window dimensions are fixed by the device's screen and can't be set on a mobile context. Choose a different device (device.deviceType) if you need another size.";
599209
+ }
599210
+ if (authoredDimensions(browser.viewport)) {
599211
+ return "browser viewport dimensions are fixed by the device's screen and can't be set on a mobile context. Choose a different device (device.deviceType) if you need another size.";
599212
+ }
599213
+ return null;
599214
+ }
599215
+ function mobileBrowserGate({ platform, browser, hasBrowserStep, hasAppStep }) {
599216
+ if (!hasBrowserStep)
599217
+ return { action: "proceed", browserName: null };
599218
+ if (hasAppStep) {
599219
+ return {
599220
+ action: "skip",
599221
+ level: "warning",
599222
+ reason: `Skipping context on '${platform}': mixing native app surfaces and browser steps in one mobile context isn't supported yet \u2014 put the web steps and the app steps in separate tests or contexts. Mobile-web-only and native-app-only contexts both run today.`
599223
+ };
599224
+ }
599225
+ const configError = mobileBrowserConfigError(browser);
599226
+ if (configError) {
599227
+ return {
599228
+ action: "fail",
599229
+ reason: `Mobile context on '${platform}': ${configError}`
599230
+ };
599231
+ }
599232
+ const browserName = browser?.name ?? defaultMobileBrowserName(platform);
599233
+ const support = mobileBrowserSupport({ platform, browserName });
599234
+ if (!support.supported) {
599235
+ return { action: "skip", level: "warning", reason: support.reason };
599236
+ }
599237
+ return { action: "proceed", browserName };
599238
+ }
599239
+ function buildMobileBrowserCapabilities({ platform, udid, cacheDir, timeout }) {
599240
+ if (platform === "android") {
599241
+ return {
599242
+ platformName: "Android",
599243
+ "appium:automationName": "UiAutomator2",
599244
+ browserName: "Chrome",
599245
+ "appium:udid": udid,
599246
+ "appium:newCommandTimeout": 600,
599247
+ // adb operations can be slow on a cold emulator (same floor as app
599248
+ // sessions).
599249
+ "appium:adbExecTimeout": 12e4,
599250
+ "wdio:enforceWebDriverClassic": true,
599251
+ // On-device chromedriver management: the server downloads a
599252
+ // chromedriver matching the device's Chrome version. Requires the
599253
+ // Appium server to run with
599254
+ // --allow-insecure=uiautomator2:chromedriver_autodownload. The
599255
+ // download lands in the Doc Detective cache so later runs reuse it.
599256
+ "appium:chromedriverAutodownload": true,
599257
+ "appium:chromedriverExecutableDir": import_node_path22.default.join(cacheDir, MOBILE_CHROMEDRIVER_DIR)
599258
+ };
599259
+ }
599260
+ const capabilities = {
599261
+ platformName: "iOS",
599262
+ "appium:automationName": "XCUITest",
599263
+ browserName: "Safari",
599264
+ "appium:udid": udid,
599265
+ "appium:newCommandTimeout": 600,
599266
+ "wdio:enforceWebDriverClassic": true
599267
+ };
599268
+ const effectiveTimeout = timeout ?? 9e5;
599269
+ capabilities["appium:wdaLaunchTimeout"] = Math.max(effectiveTimeout, 12e4);
599270
+ capabilities["appium:wdaConnectionTimeout"] = Math.max(effectiveTimeout, 12e4);
599271
+ const derivedDataPath = process.env.DOC_DETECTIVE_IOS_WDA_DERIVED_DATA_PATH;
599272
+ if (derivedDataPath && derivedDataPath.trim()) {
599273
+ capabilities["appium:derivedDataPath"] = derivedDataPath.trim();
599274
+ }
599275
+ return capabilities;
599276
+ }
599277
+
599278
+ // dist/core/tests.js
599279
+ init_cacheDir();
599280
+
598750
599281
  // dist/core/tests/iosSimulator.js
598751
599282
  var import_node_child_process11 = require("node:child_process");
598752
599283
  function parseSimctlDevices(text) {
@@ -599079,8 +599610,8 @@ function buildAcquireSimulatorDeps(log3) {
599079
599610
  // dist/core/tests/runBrowserScript.js
599080
599611
  init_validate();
599081
599612
  init_utils();
599082
- var import_node_fs24 = __toESM(require("node:fs"), 1);
599083
- var import_node_path22 = __toESM(require("node:path"), 1);
599613
+ var import_node_fs25 = __toESM(require("node:fs"), 1);
599614
+ var import_node_path23 = __toESM(require("node:path"), 1);
599084
599615
  async function runBrowserScript({ config, step, driver }) {
599085
599616
  const result = {
599086
599617
  status: "PASS",
@@ -599155,16 +599686,16 @@ async function runBrowserScript({ config, step, driver }) {
599155
599686
  }
599156
599687
  if (step.runBrowserScript.path) {
599157
599688
  try {
599158
- const dir = import_node_path22.default.dirname(step.runBrowserScript.path);
599159
- if (!import_node_fs24.default.existsSync(dir)) {
599160
- import_node_fs24.default.mkdirSync(dir, { recursive: true });
599689
+ const dir = import_node_path23.default.dirname(step.runBrowserScript.path);
599690
+ if (!import_node_fs25.default.existsSync(dir)) {
599691
+ import_node_fs25.default.mkdirSync(dir, { recursive: true });
599161
599692
  }
599162
599693
  const filePath = step.runBrowserScript.path;
599163
599694
  log(config, "debug", `Saving script result to file: ${filePath}`);
599164
- if (!import_node_fs24.default.existsSync(filePath)) {
599165
- import_node_fs24.default.writeFileSync(filePath, serialized);
599695
+ if (!import_node_fs25.default.existsSync(filePath)) {
599696
+ import_node_fs25.default.writeFileSync(filePath, serialized);
599166
599697
  } else {
599167
- const existingFile = import_node_fs24.default.readFileSync(filePath, "utf8");
599698
+ const existingFile = import_node_fs25.default.readFileSync(filePath, "utf8");
599168
599699
  const fractionalDiff = calculateFractionalDifference(existingFile, serialized);
599169
599700
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
599170
599701
  result.outputs.variation = fractionalDiff;
@@ -599174,7 +599705,7 @@ async function runBrowserScript({ config, step, driver }) {
599174
599705
  });
599175
599706
  if (fractionalDiff > step.runBrowserScript.maxVariation) {
599176
599707
  if (step.runBrowserScript.overwrite == "aboveVariation" || step.runBrowserScript.overwrite == "true") {
599177
- import_node_fs24.default.writeFileSync(filePath, serialized);
599708
+ import_node_fs25.default.writeFileSync(filePath, serialized);
599178
599709
  descriptions.push(`Saved output to file.`);
599179
599710
  } else {
599180
599711
  descriptions.push(`Didn't overwrite the existing file.`);
@@ -599182,7 +599713,7 @@ async function runBrowserScript({ config, step, driver }) {
599182
599713
  descriptions.push(`The difference between the existing output and the new output (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.runBrowserScript.maxVariation}).`);
599183
599714
  } else {
599184
599715
  if (step.runBrowserScript.overwrite == "true") {
599185
- import_node_fs24.default.writeFileSync(filePath, serialized);
599716
+ import_node_fs25.default.writeFileSync(filePath, serialized);
599186
599717
  descriptions.push(`Saved output to file.`);
599187
599718
  }
599188
599719
  }
@@ -599350,7 +599881,7 @@ async function dragAndDropElement({ config, step, driver }) {
599350
599881
  }
599351
599882
 
599352
599883
  // dist/core/tests.js
599353
- var import_node_path23 = __toESM(require("node:path"), 1);
599884
+ var import_node_path24 = __toESM(require("node:path"), 1);
599354
599885
  var import_node_child_process12 = require("node:child_process");
599355
599886
  var import_node_crypto7 = require("node:crypto");
599356
599887
  init_appium();
@@ -599504,7 +600035,7 @@ function getIntegrationConfig(config, sourceIntegration) {
599504
600035
  var import_node_http2 = __toESM(require("node:http"), 1);
599505
600036
  var import_node_https3 = __toESM(require("node:https"), 1);
599506
600037
  var import_node_url6 = require("node:url");
599507
- var __dirname5 = import_node_path23.default.dirname((0, import_node_url6.fileURLToPath)(importMetaUrl));
600038
+ var __dirname5 = import_node_path24.default.dirname((0, import_node_url6.fileURLToPath)(importMetaUrl));
599508
600039
  var KNOWN_BROWSERS = ["firefox", "chrome", "safari", "webkit"];
599509
600040
  function killTree(pid, timeoutMs = 5e3) {
599510
600041
  return new Promise((resolve) => {
@@ -599657,7 +600188,12 @@ function isDriverRequired({ test }) {
599657
600188
  }
599658
600189
  function jobDisplayResources(job, ctx) {
599659
600190
  const base = jobExclusiveResources(job, ctx);
599660
- const attemptsEmulator = job.context?.platform === "android" && !isBrowserRequired({ test: job.context });
600191
+ const attemptsEmulator = job.context?.platform === "android" && mobileBrowserGate({
600192
+ platform: "android",
600193
+ browser: job.context?.browser,
600194
+ hasBrowserStep: isBrowserRequired({ test: job.context }),
600195
+ hasAppStep: isAppDriverRequired({ test: job.context })
600196
+ }).action === "proceed";
599661
600197
  const extra = attemptsEmulator ? ["android-emulator"] : [];
599662
600198
  if (base.length || extra.length)
599663
600199
  return [.../* @__PURE__ */ new Set([...base, ...extra])];
@@ -599714,12 +600250,17 @@ function requiredAndroidOsVersions(context) {
599714
600250
  }).osVersion);
599715
600251
  }
599716
600252
  async function androidContextPreflight({ config, context, clog }) {
599717
- const hasBrowserStep = isBrowserRequired({ test: context });
599718
- if (hasBrowserStep) {
599719
- const { level, reason } = mobileContextSkipReason({
599720
- platform: "android"
599721
- });
599722
- return { ok: false, level, reason };
600253
+ const gate = mobileBrowserGate({
600254
+ platform: "android",
600255
+ browser: context.browser,
600256
+ hasBrowserStep: isBrowserRequired({ test: context }),
600257
+ hasAppStep: isAppDriverRequired({ test: context })
600258
+ });
600259
+ if (gate.action === "skip") {
600260
+ return { ok: false, level: gate.level, reason: gate.reason };
600261
+ }
600262
+ if (gate.action === "fail") {
600263
+ return { ok: false, level: "warning", reason: gate.reason, fail: true };
599723
600264
  }
599724
600265
  try {
599725
600266
  const abi = hostAbi();
@@ -599833,7 +600374,8 @@ async function androidContextPreflight({ config, context, clog }) {
599833
600374
  appiumHome: pre.appiumHome,
599834
600375
  sdkRoot: sdk.sdkRoot,
599835
600376
  deviceDeps,
599836
- warnings
600377
+ warnings,
600378
+ mobileWebBrowserName: gate.browserName
599837
600379
  };
599838
600380
  } catch (error) {
599839
600381
  return {
@@ -599844,12 +600386,17 @@ async function androidContextPreflight({ config, context, clog }) {
599844
600386
  }
599845
600387
  }
599846
600388
  async function iosContextPreflight({ config, context }) {
599847
- const hasBrowserStep = isBrowserRequired({ test: context });
599848
- if (hasBrowserStep) {
599849
- const { level, reason } = mobileContextSkipReason({
599850
- platform: "ios"
599851
- });
599852
- return { ok: false, level, reason };
600389
+ const gate = mobileBrowserGate({
600390
+ platform: "ios",
600391
+ browser: context.browser,
600392
+ hasBrowserStep: isBrowserRequired({ test: context }),
600393
+ hasAppStep: isAppDriverRequired({ test: context })
600394
+ });
600395
+ if (gate.action === "skip") {
600396
+ return { ok: false, level: gate.level, reason: gate.reason };
600397
+ }
600398
+ if (gate.action === "fail") {
600399
+ return { ok: false, level: "warning", reason: gate.reason, fail: true };
599853
600400
  }
599854
600401
  const pre = await appSurfacePreflight({ config, platform: "ios" });
599855
600402
  if (!pre.ok)
@@ -599884,7 +600431,8 @@ async function iosContextPreflight({ config, context }) {
599884
600431
  ok: true,
599885
600432
  appiumEntry: pre.appiumEntry,
599886
600433
  appiumHome: pre.appiumHome,
599887
- simulatorDeps
600434
+ simulatorDeps,
600435
+ mobileWebBrowserName: gate.browserName
599888
600436
  };
599889
600437
  }
599890
600438
  function getDefaultBrowser({ runnerDetails }) {
@@ -600089,7 +600637,7 @@ async function runSpecs({ resolvedTests }) {
600089
600637
  const runDir = getRunOutputDir(config, {
600090
600638
  create: runArchivesArtifacts(config, specs)
600091
600639
  });
600092
- const runId = import_node_path23.default.basename(runDir);
600640
+ const runId = import_node_path24.default.basename(runDir);
600093
600641
  const report = {
600094
600642
  runId,
600095
600643
  runDir,
@@ -600315,7 +600863,7 @@ async function runSpecs({ resolvedTests }) {
600315
600863
  }
600316
600864
  if (entry?.tempPath) {
600317
600865
  try {
600318
- import_node_fs25.default.unlinkSync(entry.tempPath);
600866
+ import_node_fs26.default.unlinkSync(entry.tempPath);
600319
600867
  } catch {
600320
600868
  }
600321
600869
  }
@@ -600687,6 +601235,8 @@ function selectWarmUpTargets(jobs, runnerDetails) {
600687
601235
  context.steps = [];
600688
601236
  if (!context.platform)
600689
601237
  context.platform = platform;
601238
+ if (isMobileTargetPlatform(context.platform))
601239
+ continue;
600690
601240
  if (!context.browser && isBrowserRequired({ test: context })) {
600691
601241
  context.browser = getDefaultBrowser({ runnerDetails });
600692
601242
  }
@@ -600890,7 +601440,7 @@ function buildAutoRecordStep({ config, spec, test, context }) {
600890
601440
  return null;
600891
601441
  const runDir = getRunOutputDir(config, { create: false });
600892
601442
  const contextSegment = capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context"));
600893
- const recordPath = import_node_path23.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", contextSegment, "recordings", `${contextSegment}.mp4`);
601443
+ const recordPath = import_node_path24.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), "contexts", contextSegment, "recordings", `${contextSegment}.mp4`);
600894
601444
  return {
600895
601445
  record: { path: recordPath, overwrite: "true", engine: "ffmpeg" },
600896
601446
  description: "Automatic full-context recording",
@@ -600912,7 +601462,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
600912
601462
  const action = BROWSER_STEP_KEYS.find((key) => typeof step[key] !== "undefined") || "step";
600913
601463
  const sanitizedTestId = sanitizeFilesystemName(String(test.testId ?? ""), "test");
600914
601464
  const runDir = getRunOutputDir(config);
600915
- const dir = import_node_path23.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizedTestId), "contexts", capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context")), "screenshots");
601465
+ const dir = import_node_path24.default.join(runDir, "specs", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), "tests", capPathSegment(sanitizedTestId), "contexts", capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context")), "screenshots");
600916
601466
  const stepIdString = sanitizeFilesystemName(String(step.stepId ?? ""), "step");
600917
601467
  const stepRef = capPathSegment(stepIdString.startsWith(`${sanitizedTestId}~`) ? stepIdString.slice(sanitizedTestId.length + 1) : stepIdString);
600918
601468
  const pad = Math.max(2, String(stepCount).length);
@@ -600921,7 +601471,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
600921
601471
  stepId: `${step.stepId}_auto`,
600922
601472
  description: "Automatic post-step screenshot",
600923
601473
  screenshot: {
600924
- path: import_node_path23.default.join(dir, fileName),
601474
+ path: import_node_path24.default.join(dir, fileName),
600925
601475
  overwrite: "true"
600926
601476
  }
600927
601477
  };
@@ -600934,7 +601484,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
600934
601484
  log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${captureResult.description}`);
600935
601485
  return null;
600936
601486
  }
600937
- return import_node_path23.default.relative(runDir, screenshotStep.screenshot.path).split(import_node_path23.default.sep).join("/");
601487
+ return import_node_path24.default.relative(runDir, screenshotStep.screenshot.path).split(import_node_path24.default.sep).join("/");
600938
601488
  } catch (error) {
600939
601489
  log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${error?.message ?? error}`);
600940
601490
  return null;
@@ -600955,7 +601505,7 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
600955
601505
  ...config.integrations.openApi
600956
601506
  ];
600957
601507
  }
600958
- if (!context.browser && isBrowserRequired({ test: context })) {
601508
+ if (!context.browser && isBrowserRequired({ test: context }) && !isMobileTargetPlatform(context.platform)) {
600959
601509
  context.browser = getDefaultBrowser({ runnerDetails });
600960
601510
  }
600961
601511
  const contextReport = {
@@ -600966,6 +601516,7 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
600966
601516
  };
600967
601517
  metaValues.specs[spec.specId].tests[test.testId].contexts[context.contextId] ??= { steps: {} };
600968
601518
  let appSession;
601519
+ let mobileWebBrowserName;
600969
601520
  const mobileTarget = isMobileTargetPlatform(context.platform);
600970
601521
  if (mobileTarget) {
600971
601522
  const requirementsSkip = contextRequirementsSkipMessage({ context });
@@ -600975,39 +601526,43 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
600975
601526
  contextReport.resultDescription = requirementsSkip;
600976
601527
  return contextReport;
600977
601528
  }
601529
+ const gateOutcome = (pre) => {
601530
+ clog(pre.fail ? "error" : pre.level, pre.reason);
601531
+ contextReport.result = pre.fail ? "FAIL" : "SKIPPED";
601532
+ contextReport.resultDescription = pre.reason;
601533
+ return contextReport;
601534
+ };
601535
+ let gateBrowserName;
600978
601536
  if (mobileTarget === "android") {
600979
601537
  const pre = await androidContextPreflight({ config, context, clog });
600980
- if (!pre.ok) {
600981
- clog(pre.level, pre.reason);
600982
- contextReport.result = "SKIPPED";
600983
- contextReport.resultDescription = pre.reason;
600984
- return contextReport;
600985
- }
601538
+ if (!pre.ok)
601539
+ return gateOutcome(pre);
600986
601540
  appSession = createAppSessionState();
600987
601541
  appSession.appiumEntry = pre.appiumEntry;
600988
601542
  appSession.appiumHome = pre.appiumHome;
600989
- appSession.defaultDevice = context.device;
600990
601543
  appSession.androidSdkRoot = pre.sdkRoot;
600991
601544
  appSession.androidDeviceRegistry = deviceRegistry;
600992
601545
  appSession.androidDeviceDeps = pre.deviceDeps;
600993
- contextReport.device = context.device ?? { platform: "android" };
600994
601546
  if (pre.warnings.length)
600995
601547
  contextReport.warnings = pre.warnings;
601548
+ gateBrowserName = pre.mobileWebBrowserName;
600996
601549
  } else {
600997
601550
  const pre = await iosContextPreflight({ config, context });
600998
- if (!pre.ok) {
600999
- clog(pre.level, pre.reason);
601000
- contextReport.result = "SKIPPED";
601001
- contextReport.resultDescription = pre.reason;
601002
- return contextReport;
601003
- }
601551
+ if (!pre.ok)
601552
+ return gateOutcome(pre);
601004
601553
  appSession = createAppSessionState();
601005
601554
  appSession.appiumEntry = pre.appiumEntry;
601006
601555
  appSession.appiumHome = pre.appiumHome;
601007
- appSession.defaultDevice = context.device;
601008
601556
  appSession.iosSimulatorRegistry = simulatorRegistry;
601009
601557
  appSession.iosSimulatorDeps = pre.simulatorDeps;
601010
- contextReport.device = context.device ?? { platform: "ios" };
601558
+ gateBrowserName = pre.mobileWebBrowserName;
601559
+ }
601560
+ appSession.defaultDevice = context.device;
601561
+ contextReport.device = context.device ?? { platform: mobileTarget };
601562
+ if (gateBrowserName) {
601563
+ mobileWebBrowserName = gateBrowserName;
601564
+ context.browser = { ...context.browser ?? {}, name: mobileWebBrowserName };
601565
+ contextReport.browser = context.browser;
601011
601566
  }
601012
601567
  }
601013
601568
  if (context.platform === platform) {
@@ -601087,7 +601642,7 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
601087
601642
  policy: fallbackPolicy,
601088
601643
  availableApps
601089
601644
  }) : [];
601090
- if (driverRequired && candidateEngines.length === 0) {
601645
+ if (driverRequired && !mobileWebBrowserName && candidateEngines.length === 0) {
601091
601646
  const errorMessage = freshInstallRedetected ? freshInstallOutcome === "installed" ? `Skipping context '${requestedBrowserName}' on '${context.platform}': the missing browser dependency was installed but still could not be detected.` : `Skipping context '${requestedBrowserName}' on '${context.platform}': the on-demand install/repair of its browser dependency failed.` : driverSkipDiagnostic({
601092
601647
  requestedName: requestedBrowserName ?? "<none>",
601093
601648
  // driverSkipDiagnostic treats `platform` as the *current* runner
@@ -601120,7 +601675,82 @@ ${JSON.stringify(context, null, 2)}`);
601120
601675
  throw new Error("Browser driver requested but no Appium server pool was created; the pool sizing (browserJobCount) and this context's isBrowserRequired predicate disagreed; this is a bug.");
601121
601676
  }
601122
601677
  try {
601123
- if (driverRequired) {
601678
+ if (driverRequired && mobileWebBrowserName) {
601679
+ const env = {
601680
+ APPIUM_HOME: appSession.appiumHome
601681
+ };
601682
+ if (appSession.androidSdkRoot) {
601683
+ env.ANDROID_HOME = appSession.androidSdkRoot;
601684
+ env.ANDROID_SDK_ROOT = appSession.androidSdkRoot;
601685
+ }
601686
+ const extraArgs = mobileTarget === "android" ? ["--allow-insecure", "uiautomator2:chromedriver_autodownload"] : [];
601687
+ let acquired;
601688
+ try {
601689
+ const server = await startAppiumServer(appSession.appiumEntry, config, void 0, env, extraArgs);
601690
+ appSession.server = { port: server.port, process: server.process };
601691
+ const desc = normalizeDeviceDescriptor({
601692
+ contextDevice: context.device,
601693
+ platform: mobileTarget
601694
+ });
601695
+ acquired = mobileTarget === "android" ? await acquireDevice({
601696
+ desc,
601697
+ registry: appSession.androidDeviceRegistry,
601698
+ sdkRoot: appSession.androidSdkRoot,
601699
+ deps: appSession.androidDeviceDeps
601700
+ }) : await acquireSimulator({
601701
+ desc,
601702
+ registry: appSession.iosSimulatorRegistry,
601703
+ deps: appSession.iosSimulatorDeps
601704
+ });
601705
+ } catch (error) {
601706
+ const errorMessage = `Skipping context on '${mobileTarget}': couldn't prepare the device for the ${mobileWebBrowserName} session (${error?.message ?? error}). Check the emulator/simulator toolchain (\`doc-detective install ${mobileTarget}\`).`;
601707
+ clog("warning", errorMessage);
601708
+ contextReport.result = "SKIPPED";
601709
+ contextReport.resultDescription = errorMessage;
601710
+ return contextReport;
601711
+ }
601712
+ if ("skip" in acquired) {
601713
+ clog("warning", acquired.skip);
601714
+ contextReport.result = "SKIPPED";
601715
+ contextReport.resultDescription = acquired.skip;
601716
+ return contextReport;
601717
+ }
601718
+ contextReport.device = {
601719
+ ...typeof contextReport.device === "object" ? contextReport.device : {},
601720
+ platform: mobileTarget,
601721
+ name: acquired.entry.name
601722
+ };
601723
+ const capabilities = buildMobileBrowserCapabilities({
601724
+ platform: mobileTarget,
601725
+ udid: acquired.entry.udid,
601726
+ // The resolved cache root (not the raw config field, which is
601727
+ // usually unset) — the chromedriver autodownload dir lives here.
601728
+ cacheDir: getCacheDir({ cacheDir: config?.cacheDir })
601729
+ });
601730
+ try {
601731
+ driver = await driverStart(capabilities, appSession.server.port, 2, {
601732
+ cacheDir: config?.cacheDir
601733
+ });
601734
+ } catch (error) {
601735
+ const hint = mobileTarget === "android" ? " Chrome may not be present on this emulator image \u2014 managed Android mobile-web needs a `google_apis` system image (see `doc-detective install android`)." : " Check that the simulator runtime includes Safari and that WebDriverAgent can build (see `doc-detective install ios`).";
601736
+ const errorMessage = `Skipping context on '${mobileTarget}': couldn't start the ${mobileWebBrowserName} session on device '${acquired.entry.name}' (${error?.message ?? error}).${hint}`;
601737
+ clog("warning", errorMessage);
601738
+ contextReport.result = "SKIPPED";
601739
+ contextReport.resultDescription = errorMessage;
601740
+ return contextReport;
601741
+ }
601742
+ browserSessions = createSessionRegistry({
601743
+ open: async () => {
601744
+ throw new Error("Additional browser sessions aren't supported on a managed device; the device browser is the context's only browser surface.");
601745
+ },
601746
+ isNameTaken: (name) => !!processRegistry?.has(name)
601747
+ });
601748
+ registerSession(browserSessions, {
601749
+ name: String(mobileWebBrowserName).toLowerCase(),
601750
+ engine: String(mobileWebBrowserName).toLowerCase(),
601751
+ driver
601752
+ });
601753
+ } else if (driverRequired) {
601124
601754
  appiumPort = await appiumPool.acquire();
601125
601755
  if (portToDisplay) {
601126
601756
  const display = portToDisplay.get(appiumPort);
@@ -601789,7 +602419,7 @@ async function runStep({ config = {}, context = {}, step, driver, metaValues = {
601789
602419
  }
601790
602420
  return actionResult;
601791
602421
  }
601792
- async function startAppiumServer(appiumEntry, config, display, extraEnv) {
602422
+ async function startAppiumServer(appiumEntry, config, display, extraEnv, extraArgs) {
601793
602423
  const port = await findFreePort();
601794
602424
  log(config, "debug", `Starting Appium on port ${port}`);
601795
602425
  const env = display || extraEnv ? {
@@ -601797,9 +602427,9 @@ async function startAppiumServer(appiumEntry, config, display, extraEnv) {
601797
602427
  ...display ? { DISPLAY: display } : {},
601798
602428
  ...extraEnv ?? {}
601799
602429
  } : process.env;
601800
- const proc = (0, import_node_child_process12.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(port)], {
602430
+ const proc = (0, import_node_child_process12.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(port), ...extraArgs ?? []], {
601801
602431
  windowsHide: true,
601802
- cwd: import_node_path23.default.join(__dirname5, "../.."),
602432
+ cwd: import_node_path24.default.join(__dirname5, "../.."),
601803
602433
  env
601804
602434
  });
601805
602435
  proc.on("error", (err) => {
@@ -601946,7 +602576,7 @@ async function getRunner(options = {}) {
601946
602576
  }
601947
602577
  const appium = (0, import_node_child_process12.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(appiumPort)], {
601948
602578
  windowsHide: true,
601949
- cwd: import_node_path23.default.join(__dirname5, "../..")
602579
+ cwd: import_node_path24.default.join(__dirname5, "../..")
601950
602580
  });
601951
602581
  appium.on("error", (err) => {
601952
602582
  log(config, "warning", `Appium process error: ${err?.stack ?? err?.message ?? String(err)}`);