doc-detective 4.29.0 → 4.30.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 (87) hide show
  1. package/bin/doc-detective-lsp.js +6 -0
  2. package/dist/cli.js +58 -35
  3. package/dist/cli.js.map +1 -1
  4. package/dist/common/src/schemas/schemas.json +56 -56
  5. package/dist/common/src/validate.d.ts +16 -1
  6. package/dist/common/src/validate.d.ts.map +1 -1
  7. package/dist/common/src/validate.js +113 -13
  8. package/dist/common/src/validate.js.map +1 -1
  9. package/dist/core/config.d.ts +66 -1
  10. package/dist/core/config.d.ts.map +1 -1
  11. package/dist/core/config.js +121 -68
  12. package/dist/core/config.js.map +1 -1
  13. package/dist/core/detectTests.d.ts.map +1 -1
  14. package/dist/core/detectTests.js +105 -23
  15. package/dist/core/detectTests.js.map +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/core/index.js +53 -14
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/core/openapi.d.ts.map +1 -1
  20. package/dist/core/openapi.js +22 -5
  21. package/dist/core/openapi.js.map +1 -1
  22. package/dist/core/resolveTests.d.ts.map +1 -1
  23. package/dist/core/resolveTests.js +33 -2
  24. package/dist/core/resolveTests.js.map +1 -1
  25. package/dist/core/tests/findStrategies.d.ts.map +1 -1
  26. package/dist/core/tests/findStrategies.js +83 -10
  27. package/dist/core/tests/findStrategies.js.map +1 -1
  28. package/dist/core/tests/saveScreenshot.d.ts.map +1 -1
  29. package/dist/core/tests/saveScreenshot.js +101 -59
  30. package/dist/core/tests/saveScreenshot.js.map +1 -1
  31. package/dist/core/utils.d.ts.map +1 -1
  32. package/dist/core/utils.js +13 -4
  33. package/dist/core/utils.js.map +1 -1
  34. package/dist/index.cjs +356 -196
  35. package/dist/lsp/command.d.ts +12 -0
  36. package/dist/lsp/command.d.ts.map +1 -0
  37. package/dist/lsp/command.js +24 -0
  38. package/dist/lsp/command.js.map +1 -0
  39. package/dist/lsp/completion.d.ts +27 -0
  40. package/dist/lsp/completion.d.ts.map +1 -0
  41. package/dist/lsp/completion.js +114 -0
  42. package/dist/lsp/completion.js.map +1 -0
  43. package/dist/lsp/diagnostics.d.ts +21 -0
  44. package/dist/lsp/diagnostics.d.ts.map +1 -0
  45. package/dist/lsp/diagnostics.js +137 -0
  46. package/dist/lsp/diagnostics.js.map +1 -0
  47. package/dist/lsp/gate.d.ts +30 -0
  48. package/dist/lsp/gate.d.ts.map +1 -0
  49. package/dist/lsp/gate.js +80 -0
  50. package/dist/lsp/gate.js.map +1 -0
  51. package/dist/lsp/hover.d.ts +9 -0
  52. package/dist/lsp/hover.d.ts.map +1 -0
  53. package/dist/lsp/hover.js +56 -0
  54. package/dist/lsp/hover.js.map +1 -0
  55. package/dist/lsp/inline.d.ts +25 -0
  56. package/dist/lsp/inline.d.ts.map +1 -0
  57. package/dist/lsp/inline.js +208 -0
  58. package/dist/lsp/inline.js.map +1 -0
  59. package/dist/lsp/json/positions.d.ts +51 -0
  60. package/dist/lsp/json/positions.d.ts.map +1 -0
  61. package/dist/lsp/json/positions.js +101 -0
  62. package/dist/lsp/json/positions.js.map +1 -0
  63. package/dist/lsp/messages.d.ts +27 -0
  64. package/dist/lsp/messages.d.ts.map +1 -0
  65. package/dist/lsp/messages.js +33 -0
  66. package/dist/lsp/messages.js.map +1 -0
  67. package/dist/lsp/model.d.ts +27 -0
  68. package/dist/lsp/model.d.ts.map +1 -0
  69. package/dist/lsp/model.js +49 -0
  70. package/dist/lsp/model.js.map +1 -0
  71. package/dist/lsp/registry.d.ts +61 -0
  72. package/dist/lsp/registry.d.ts.map +1 -0
  73. package/dist/lsp/registry.js +124 -0
  74. package/dist/lsp/registry.js.map +1 -0
  75. package/dist/lsp/server.d.ts +40 -0
  76. package/dist/lsp/server.d.ts.map +1 -0
  77. package/dist/lsp/server.js +64 -0
  78. package/dist/lsp/server.js.map +1 -0
  79. package/dist/lsp/yaml/positions.d.ts +32 -0
  80. package/dist/lsp/yaml/positions.d.ts.map +1 -0
  81. package/dist/lsp/yaml/positions.js +92 -0
  82. package/dist/lsp/yaml/positions.js.map +1 -0
  83. package/dist/utils.d.ts.map +1 -1
  84. package/dist/utils.js +30 -5
  85. package/dist/utils.js.map +1 -1
  86. package/package.json +6 -2
  87. package/scripts/postinstall.js +144 -1
package/dist/index.cjs CHANGED
@@ -9324,7 +9324,7 @@ var init_schemas = __esm({
9324
9324
  },
9325
9325
  elementText: {
9326
9326
  type: "string",
9327
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
9327
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
9328
9328
  },
9329
9329
  selector: {
9330
9330
  type: "string",
@@ -13470,7 +13470,7 @@ var init_schemas = __esm({
13470
13470
  },
13471
13471
  elementText: {
13472
13472
  type: "string",
13473
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
13473
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
13474
13474
  },
13475
13475
  selector: {
13476
13476
  type: "string",
@@ -18701,7 +18701,7 @@ var init_schemas = __esm({
18701
18701
  },
18702
18702
  elementText: {
18703
18703
  type: "string",
18704
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
18704
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
18705
18705
  },
18706
18706
  elementId: {
18707
18707
  type: "string",
@@ -19233,7 +19233,7 @@ var init_schemas = __esm({
19233
19233
  },
19234
19234
  elementText: {
19235
19235
  type: "string",
19236
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
19236
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
19237
19237
  },
19238
19238
  elementId: {
19239
19239
  type: "string",
@@ -54963,7 +54963,7 @@ var init_schemas = __esm({
54963
54963
  },
54964
54964
  elementText: {
54965
54965
  type: "string",
54966
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
54966
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
54967
54967
  },
54968
54968
  selector: {
54969
54969
  type: "string",
@@ -59109,7 +59109,7 @@ var init_schemas = __esm({
59109
59109
  },
59110
59110
  elementText: {
59111
59111
  type: "string",
59112
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
59112
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
59113
59113
  },
59114
59114
  selector: {
59115
59115
  type: "string",
@@ -64340,7 +64340,7 @@ var init_schemas = __esm({
64340
64340
  },
64341
64341
  elementText: {
64342
64342
  type: "string",
64343
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
64343
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
64344
64344
  },
64345
64345
  elementId: {
64346
64346
  type: "string",
@@ -64872,7 +64872,7 @@ var init_schemas = __esm({
64872
64872
  },
64873
64873
  elementText: {
64874
64874
  type: "string",
64875
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
64875
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
64876
64876
  },
64877
64877
  elementId: {
64878
64878
  type: "string",
@@ -95536,7 +95536,7 @@ var init_schemas = __esm({
95536
95536
  },
95537
95537
  elementText: {
95538
95538
  type: "string",
95539
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
95539
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
95540
95540
  },
95541
95541
  selector: {
95542
95542
  type: "string",
@@ -99682,7 +99682,7 @@ var init_schemas = __esm({
99682
99682
  },
99683
99683
  elementText: {
99684
99684
  type: "string",
99685
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
99685
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
99686
99686
  },
99687
99687
  selector: {
99688
99688
  type: "string",
@@ -103964,7 +103964,7 @@ var init_schemas = __esm({
103964
103964
  },
103965
103965
  elementText: {
103966
103966
  type: "string",
103967
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
103967
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
103968
103968
  },
103969
103969
  elementId: {
103970
103970
  type: "string",
@@ -104496,7 +104496,7 @@ var init_schemas = __esm({
104496
104496
  },
104497
104497
  elementText: {
104498
104498
  type: "string",
104499
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
104499
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
104500
104500
  },
104501
104501
  elementId: {
104502
104502
  type: "string",
@@ -113889,7 +113889,7 @@ var init_schemas = __esm({
113889
113889
  },
113890
113890
  elementText: {
113891
113891
  type: "string",
113892
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
113892
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
113893
113893
  },
113894
113894
  selector: {
113895
113895
  type: "string",
@@ -118035,7 +118035,7 @@ var init_schemas = __esm({
118035
118035
  },
118036
118036
  elementText: {
118037
118037
  type: "string",
118038
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
118038
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
118039
118039
  },
118040
118040
  selector: {
118041
118041
  type: "string",
@@ -123266,7 +123266,7 @@ var init_schemas = __esm({
123266
123266
  },
123267
123267
  elementText: {
123268
123268
  type: "string",
123269
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
123269
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
123270
123270
  },
123271
123271
  elementId: {
123272
123272
  type: "string",
@@ -123798,7 +123798,7 @@ var init_schemas = __esm({
123798
123798
  },
123799
123799
  elementText: {
123800
123800
  type: "string",
123801
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
123801
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
123802
123802
  },
123803
123803
  elementId: {
123804
123804
  type: "string",
@@ -159528,7 +159528,7 @@ var init_schemas = __esm({
159528
159528
  },
159529
159529
  elementText: {
159530
159530
  type: "string",
159531
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
159531
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
159532
159532
  },
159533
159533
  selector: {
159534
159534
  type: "string",
@@ -163674,7 +163674,7 @@ var init_schemas = __esm({
163674
163674
  },
163675
163675
  elementText: {
163676
163676
  type: "string",
163677
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
163677
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
163678
163678
  },
163679
163679
  selector: {
163680
163680
  type: "string",
@@ -168905,7 +168905,7 @@ var init_schemas = __esm({
168905
168905
  },
168906
168906
  elementText: {
168907
168907
  type: "string",
168908
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
168908
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
168909
168909
  },
168910
168910
  elementId: {
168911
168911
  type: "string",
@@ -169437,7 +169437,7 @@ var init_schemas = __esm({
169437
169437
  },
169438
169438
  elementText: {
169439
169439
  type: "string",
169440
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
169440
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
169441
169441
  },
169442
169442
  elementId: {
169443
169443
  type: "string",
@@ -206906,7 +206906,7 @@ var init_schemas = __esm({
206906
206906
  },
206907
206907
  elementText: {
206908
206908
  type: "string",
206909
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
206909
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
206910
206910
  },
206911
206911
  selector: {
206912
206912
  type: "string",
@@ -211052,7 +211052,7 @@ var init_schemas = __esm({
211052
211052
  },
211053
211053
  elementText: {
211054
211054
  type: "string",
211055
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
211055
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
211056
211056
  },
211057
211057
  selector: {
211058
211058
  type: "string",
@@ -216283,7 +216283,7 @@ var init_schemas = __esm({
216283
216283
  },
216284
216284
  elementText: {
216285
216285
  type: "string",
216286
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
216286
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
216287
216287
  },
216288
216288
  elementId: {
216289
216289
  type: "string",
@@ -216815,7 +216815,7 @@ var init_schemas = __esm({
216815
216815
  },
216816
216816
  elementText: {
216817
216817
  type: "string",
216818
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
216818
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
216819
216819
  },
216820
216820
  elementId: {
216821
216821
  type: "string",
@@ -251807,7 +251807,7 @@ var init_schemas = __esm({
251807
251807
  },
251808
251808
  elementText: {
251809
251809
  type: "string",
251810
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
251810
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
251811
251811
  },
251812
251812
  selector: {
251813
251813
  type: "string",
@@ -255953,7 +255953,7 @@ var init_schemas = __esm({
255953
255953
  },
255954
255954
  elementText: {
255955
255955
  type: "string",
255956
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
255956
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
255957
255957
  },
255958
255958
  selector: {
255959
255959
  type: "string",
@@ -261184,7 +261184,7 @@ var init_schemas = __esm({
261184
261184
  },
261185
261185
  elementText: {
261186
261186
  type: "string",
261187
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
261187
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
261188
261188
  },
261189
261189
  elementId: {
261190
261190
  type: "string",
@@ -261716,7 +261716,7 @@ var init_schemas = __esm({
261716
261716
  },
261717
261717
  elementText: {
261718
261718
  type: "string",
261719
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
261719
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
261720
261720
  },
261721
261721
  elementId: {
261722
261722
  type: "string",
@@ -299669,7 +299669,7 @@ var init_schemas = __esm({
299669
299669
  },
299670
299670
  elementText: {
299671
299671
  type: "string",
299672
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
299672
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
299673
299673
  },
299674
299674
  selector: {
299675
299675
  type: "string",
@@ -303815,7 +303815,7 @@ var init_schemas = __esm({
303815
303815
  },
303816
303816
  elementText: {
303817
303817
  type: "string",
303818
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
303818
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
303819
303819
  },
303820
303820
  selector: {
303821
303821
  type: "string",
@@ -309046,7 +309046,7 @@ var init_schemas = __esm({
309046
309046
  },
309047
309047
  elementText: {
309048
309048
  type: "string",
309049
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
309049
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
309050
309050
  },
309051
309051
  elementId: {
309052
309052
  type: "string",
@@ -309578,7 +309578,7 @@ var init_schemas = __esm({
309578
309578
  },
309579
309579
  elementText: {
309580
309580
  type: "string",
309581
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
309581
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
309582
309582
  },
309583
309583
  elementId: {
309584
309584
  type: "string",
@@ -344570,7 +344570,7 @@ var init_schemas = __esm({
344570
344570
  },
344571
344571
  elementText: {
344572
344572
  type: "string",
344573
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
344573
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
344574
344574
  },
344575
344575
  selector: {
344576
344576
  type: "string",
@@ -348716,7 +348716,7 @@ var init_schemas = __esm({
348716
348716
  },
348717
348717
  elementText: {
348718
348718
  type: "string",
348719
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
348719
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
348720
348720
  },
348721
348721
  selector: {
348722
348722
  type: "string",
@@ -353947,7 +353947,7 @@ var init_schemas = __esm({
353947
353947
  },
353948
353948
  elementText: {
353949
353949
  type: "string",
353950
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
353950
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
353951
353951
  },
353952
353952
  elementId: {
353953
353953
  type: "string",
@@ -354479,7 +354479,7 @@ var init_schemas = __esm({
354479
354479
  },
354480
354480
  elementText: {
354481
354481
  type: "string",
354482
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
354482
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
354483
354483
  },
354484
354484
  elementId: {
354485
354485
  type: "string",
@@ -395756,7 +395756,7 @@ var init_schemas = __esm({
395756
395756
  },
395757
395757
  elementText: {
395758
395758
  type: "string",
395759
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
395759
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
395760
395760
  },
395761
395761
  selector: {
395762
395762
  type: "string",
@@ -399902,7 +399902,7 @@ var init_schemas = __esm({
399902
399902
  },
399903
399903
  elementText: {
399904
399904
  type: "string",
399905
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
399905
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
399906
399906
  },
399907
399907
  selector: {
399908
399908
  type: "string",
@@ -405133,7 +405133,7 @@ var init_schemas = __esm({
405133
405133
  },
405134
405134
  elementText: {
405135
405135
  type: "string",
405136
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
405136
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
405137
405137
  },
405138
405138
  elementId: {
405139
405139
  type: "string",
@@ -405665,7 +405665,7 @@ var init_schemas = __esm({
405665
405665
  },
405666
405666
  elementText: {
405667
405667
  type: "string",
405668
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
405668
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
405669
405669
  },
405670
405670
  elementId: {
405671
405671
  type: "string",
@@ -440657,7 +440657,7 @@ var init_schemas = __esm({
440657
440657
  },
440658
440658
  elementText: {
440659
440659
  type: "string",
440660
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
440660
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
440661
440661
  },
440662
440662
  selector: {
440663
440663
  type: "string",
@@ -444803,7 +444803,7 @@ var init_schemas = __esm({
444803
444803
  },
444804
444804
  elementText: {
444805
444805
  type: "string",
444806
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
444806
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
444807
444807
  },
444808
444808
  selector: {
444809
444809
  type: "string",
@@ -450034,7 +450034,7 @@ var init_schemas = __esm({
450034
450034
  },
450035
450035
  elementText: {
450036
450036
  type: "string",
450037
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
450037
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
450038
450038
  },
450039
450039
  elementId: {
450040
450040
  type: "string",
@@ -450566,7 +450566,7 @@ var init_schemas = __esm({
450566
450566
  },
450567
450567
  elementText: {
450568
450568
  type: "string",
450569
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
450569
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
450570
450570
  },
450571
450571
  elementId: {
450572
450572
  type: "string",
@@ -487250,7 +487250,7 @@ var init_schemas = __esm({
487250
487250
  },
487251
487251
  elementText: {
487252
487252
  type: "string",
487253
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
487253
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
487254
487254
  },
487255
487255
  selector: {
487256
487256
  type: "string",
@@ -491396,7 +491396,7 @@ var init_schemas = __esm({
491396
491396
  },
491397
491397
  elementText: {
491398
491398
  type: "string",
491399
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
491399
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
491400
491400
  },
491401
491401
  selector: {
491402
491402
  type: "string",
@@ -496627,7 +496627,7 @@ var init_schemas = __esm({
496627
496627
  },
496628
496628
  elementText: {
496629
496629
  type: "string",
496630
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
496630
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
496631
496631
  },
496632
496632
  elementId: {
496633
496633
  type: "string",
@@ -497159,7 +497159,7 @@ var init_schemas = __esm({
497159
497159
  },
497160
497160
  elementText: {
497161
497161
  type: "string",
497162
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
497162
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
497163
497163
  },
497164
497164
  elementId: {
497165
497165
  type: "string",
@@ -535496,7 +535496,7 @@ var init_schemas = __esm({
535496
535496
  },
535497
535497
  elementText: {
535498
535498
  type: "string",
535499
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
535499
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
535500
535500
  },
535501
535501
  selector: {
535502
535502
  type: "string",
@@ -539642,7 +539642,7 @@ var init_schemas = __esm({
539642
539642
  },
539643
539643
  elementText: {
539644
539644
  type: "string",
539645
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
539645
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
539646
539646
  },
539647
539647
  selector: {
539648
539648
  type: "string",
@@ -544873,7 +544873,7 @@ var init_schemas = __esm({
544873
544873
  },
544874
544874
  elementText: {
544875
544875
  type: "string",
544876
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
544876
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
544877
544877
  },
544878
544878
  elementId: {
544879
544879
  type: "string",
@@ -545405,7 +545405,7 @@ var init_schemas = __esm({
545405
545405
  },
545406
545406
  elementText: {
545407
545407
  type: "string",
545408
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
545408
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
545409
545409
  },
545410
545410
  elementId: {
545411
545411
  type: "string",
@@ -580397,7 +580397,7 @@ var init_schemas = __esm({
580397
580397
  },
580398
580398
  elementText: {
580399
580399
  type: "string",
580400
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
580400
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
580401
580401
  },
580402
580402
  selector: {
580403
580403
  type: "string",
@@ -584543,7 +584543,7 @@ var init_schemas = __esm({
584543
584543
  },
584544
584544
  elementText: {
584545
584545
  type: "string",
584546
- description: "Display text of the element to find. If combined with other element finding fields, the element must match all specified criteria."
584546
+ description: "Display text of the element to find. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed to single spaces), so text a framework splits across several nodes still matches and a driver's surrounding whitespace doesn't cause a miss. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text. If combined with other element finding fields, the element must match all specified criteria."
584547
584547
  },
584548
584548
  selector: {
584549
584549
  type: "string",
@@ -589774,7 +589774,7 @@ var init_schemas = __esm({
589774
589774
  },
589775
589775
  elementText: {
589776
589776
  type: "string",
589777
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
589777
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
589778
589778
  },
589779
589779
  elementId: {
589780
589780
  type: "string",
@@ -590306,7 +590306,7 @@ var init_schemas = __esm({
590306
590306
  },
590307
590307
  elementText: {
590308
590308
  type: "string",
590309
- description: "Text content the element must contain. Supports exact match or regex pattern using /pattern/ syntax."
590309
+ description: "Display text of the element to wait for. Matched against the element's full visible text with whitespace normalized (leading/trailing trimmed, internal runs collapsed), so framework-fragmented text still matches. Wrap the value in slashes (`/pattern/`) to match a substring by regular expression instead of the whole normalized text."
590310
590310
  },
590311
590311
  elementId: {
590312
590312
  type: "string",
@@ -631868,10 +631868,16 @@ function getRandomUUID() {
631868
631868
  return v.toString(16);
631869
631869
  });
631870
631870
  }
631871
+ function cloneForValidation(object) {
631872
+ return JSON.parse(JSON.stringify(object));
631873
+ }
631874
+ function snapshotErrors(errors) {
631875
+ return errors.map((error) => ({ ...error }));
631876
+ }
631871
631877
  function escapeRegExp(string) {
631872
631878
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
631873
631879
  }
631874
- function validate({ schemaKey, object, addDefaults = true }) {
631880
+ function validate({ schemaKey, object, addDefaults = true, structuredErrors = false }) {
631875
631881
  if (!schemaKey) {
631876
631882
  throw new Error("Schema key is required.");
631877
631883
  }
@@ -631883,6 +631889,9 @@ function validate({ schemaKey, object, addDefaults = true }) {
631883
631889
  errors: "",
631884
631890
  object
631885
631891
  };
631892
+ if (structuredErrors) {
631893
+ result.errorObjects = [];
631894
+ }
631886
631895
  let validationObject;
631887
631896
  let check = ajv.getSchema(schemaKey);
631888
631897
  if (!check) {
@@ -631891,29 +631900,43 @@ function validate({ schemaKey, object, addDefaults = true }) {
631891
631900
  result.object = object;
631892
631901
  return result;
631893
631902
  }
631894
- validationObject = JSON.parse(JSON.stringify(object));
631903
+ validationObject = cloneForValidation(object);
631895
631904
  result.valid = check(validationObject);
631896
631905
  result.errors = "";
631897
631906
  if (check.errors) {
631907
+ const targetErrors = check.errors;
631898
631908
  const compatibleSchemasList = compatibleSchemas[schemaKey];
631899
631909
  if (!compatibleSchemasList) {
631900
- result.errors = check.errors.map((error) => `${error.instancePath} ${error.message} (${JSON.stringify(error.params)})`).join(", ");
631910
+ result.errors = targetErrors.map((error) => `${error.instancePath} ${error.message} (${JSON.stringify(error.params)})`).join(", ");
631911
+ if (structuredErrors) {
631912
+ result.errorObjects = snapshotErrors(targetErrors);
631913
+ }
631901
631914
  result.object = object;
631902
631915
  result.valid = false;
631903
631916
  return result;
631904
631917
  }
631905
- const matchedSchemaKey = compatibleSchemasList.find((key) => {
631906
- validationObject = JSON.parse(JSON.stringify(object));
631907
- const check2 = ajv.getSchema(key);
631908
- if (check2 && check2(validationObject))
631909
- return key;
631918
+ let matchedSchemaKey = compatibleSchemasList.find((key) => {
631919
+ const probe = ajvCheck.getSchema(key);
631920
+ return probe(object);
631910
631921
  });
631911
631922
  if (!matchedSchemaKey) {
631912
- result.errors = check.errors.map((error) => `${error.instancePath} ${error.message} (${JSON.stringify(error.params)})`).join(", ");
631923
+ matchedSchemaKey = compatibleSchemasList.find((key) => {
631924
+ const mutatingCheck = ajv.getSchema(key);
631925
+ return mutatingCheck(cloneForValidation(object));
631926
+ });
631927
+ }
631928
+ if (!matchedSchemaKey) {
631929
+ result.errors = targetErrors.map((error) => `${error.instancePath} ${error.message} (${JSON.stringify(error.params)})`).join(", ");
631930
+ if (structuredErrors) {
631931
+ result.errorObjects = snapshotErrors(targetErrors);
631932
+ }
631913
631933
  result.object = object;
631914
631934
  result.valid = false;
631915
631935
  return result;
631916
631936
  } else {
631937
+ validationObject = cloneForValidation(object);
631938
+ const matchedCheck = ajv.getSchema(matchedSchemaKey);
631939
+ matchedCheck(validationObject);
631917
631940
  const transformedObject = transformToSchemaKey({
631918
631941
  currentSchema: matchedSchemaKey,
631919
631942
  targetSchema: schemaKey,
@@ -632270,7 +632293,7 @@ Errors: ${result.errors}`);
632270
632293
  }
632271
632294
  return null;
632272
632295
  }
632273
- var import_ajv, import_ajv_formats, import_ajv_keywords, import_ajv_errors, import_dynamicDefaults, ajv, compatibleSchemas;
632296
+ var import_ajv, import_ajv_formats, import_ajv_keywords, import_ajv_errors, import_dynamicDefaults, ajv, ajvCheck, compatibleSchemas;
632274
632297
  var init_validate = __esm({
632275
632298
  "dist/common/src/validate.js"() {
632276
632299
  "use strict";
@@ -632287,12 +632310,23 @@ var init_validate = __esm({
632287
632310
  allowUnionTypes: true,
632288
632311
  coerceTypes: true
632289
632312
  });
632313
+ ajvCheck = new import_ajv.default({
632314
+ strictSchema: false,
632315
+ useDefaults: false,
632316
+ allErrors: true,
632317
+ allowUnionTypes: true,
632318
+ coerceTypes: false
632319
+ });
632290
632320
  import_dynamicDefaults.default.DEFAULTS.uuid = (_args) => getRandomUUID;
632291
632321
  (0, import_ajv_formats.default)(ajv);
632292
632322
  (0, import_ajv_keywords.default)(ajv);
632293
632323
  (0, import_ajv_errors.default)(ajv);
632324
+ (0, import_ajv_formats.default)(ajvCheck);
632325
+ (0, import_ajv_keywords.default)(ajvCheck);
632326
+ (0, import_ajv_errors.default)(ajvCheck);
632294
632327
  for (const [key, value] of Object.entries(schemas)) {
632295
632328
  ajv.addSchema(value, key);
632329
+ ajvCheck.addSchema(value, key);
632296
632330
  }
632297
632331
  compatibleSchemas = {
632298
632332
  config_v3: ["config_v2"],
@@ -633722,8 +633756,8 @@ function waitForStdio(bg, expected, { deadline }) {
633722
633756
  return Promise.reject(new Error(`waitUntil.stdio: invalid regular expression ${expected}: ${error.message}`));
633723
633757
  }
633724
633758
  }
633725
- const matchesText = (text) => regex ? regex.test(text) : text.includes(expected);
633726
- const matched = () => matchesText(bg.getStdout()) || matchesText(bg.getStderr());
633759
+ const matchesText2 = (text) => regex ? regex.test(text) : text.includes(expected);
633760
+ const matched = () => matchesText2(bg.getStdout()) || matchesText2(bg.getStderr());
633727
633761
  return new Promise((resolve, reject) => {
633728
633762
  if (matched())
633729
633763
  return resolve();
@@ -634062,13 +634096,14 @@ async function loadEnvs(envsFile) {
634062
634096
  }
634063
634097
  }
634064
634098
  function logLevelEnabled(config, level) {
634065
- if (config.logLevel === "error" && level === "error")
634099
+ const currentLevel = config.logLevel || "info";
634100
+ if (currentLevel === "error" && level === "error")
634066
634101
  return true;
634067
- if (config.logLevel === "warning" && (level === "error" || level === "warning"))
634102
+ if (currentLevel === "warning" && (level === "error" || level === "warning"))
634068
634103
  return true;
634069
- if (config.logLevel === "info" && (level === "error" || level === "warning" || level === "info"))
634104
+ if (currentLevel === "info" && (level === "error" || level === "warning" || level === "info"))
634070
634105
  return true;
634071
- if (config.logLevel === "debug" && (level === "error" || level === "warning" || level === "info" || level === "debug"))
634106
+ if (currentLevel === "debug" && (level === "error" || level === "warning" || level === "info" || level === "debug"))
634072
634107
  return true;
634073
634108
  return false;
634074
634109
  }
@@ -635100,12 +635135,19 @@ var init_files = __esm({
635100
635135
  });
635101
635136
 
635102
635137
  // dist/core/openapi.js
635138
+ function getGenerator() {
635139
+ if (!jsfPromise) {
635140
+ jsfPromise = import("json-schema-faker").then(({ createGenerator }) => createGenerator({ optionalsProbability: 0 }));
635141
+ }
635142
+ return jsfPromise;
635143
+ }
635103
635144
  async function loadDescription(descriptionPath = "") {
635104
635145
  if (!descriptionPath) {
635105
635146
  throw new Error("Description is required.");
635106
635147
  }
635107
635148
  const definition = await readFile({ fileURLOrPath: descriptionPath });
635108
- const dereferencedDefinition = await import_json_schema_ref_parser.default.dereference(definition);
635149
+ const parser = (await import("@apidevtools/json-schema-ref-parser")).default;
635150
+ const dereferencedDefinition = await parser.dereference(definition);
635109
635151
  return dereferencedDefinition;
635110
635152
  }
635111
635153
  async function getOperation(definition = {}, operationId = "", responseCode = "", exampleKey = "", server = "") {
@@ -635229,7 +635271,8 @@ async function getExample(definition = {}, exampleKey = "", generateFromSchema =
635229
635271
  }
635230
635272
  if (generateFromSchema && definition.type) {
635231
635273
  try {
635232
- example = await jsf.generate(definition);
635274
+ const generator = await getGenerator();
635275
+ example = await generator.generate(definition);
635233
635276
  if (example)
635234
635277
  return example;
635235
635278
  } catch (error) {
@@ -635304,15 +635347,12 @@ function checkForExamples(definition = {}, exampleKey = "") {
635304
635347
  return true;
635305
635348
  return false;
635306
635349
  }
635307
- var import_json_schema_faker, import_json_schema_ref_parser, jsf;
635350
+ var jsfPromise;
635308
635351
  var init_openapi = __esm({
635309
635352
  "dist/core/openapi.js"() {
635310
635353
  "use strict";
635311
635354
  init_utils();
635312
- import_json_schema_faker = require("json-schema-faker");
635313
635355
  init_files();
635314
- import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"), 1);
635315
- jsf = (0, import_json_schema_faker.createGenerator)({ optionalsProbability: 0 });
635316
635356
  }
635317
635357
  });
635318
635358
 
@@ -635320,9 +635360,11 @@ var init_openapi = __esm({
635320
635360
  var config_exports = {};
635321
635361
  __export(config_exports, {
635322
635362
  clearAppCache: () => clearAppCache,
635363
+ detectInstalledBrowserDrivers: () => detectInstalledBrowserDrivers,
635323
635364
  getAvailableApps: () => getAvailableApps,
635324
635365
  getBrowserDiagnostics: () => getBrowserDiagnostics,
635325
635366
  getEnvironment: () => getEnvironment,
635367
+ patchAppCache: () => patchAppCache,
635326
635368
  resolveConcurrentRunners: () => resolveConcurrentRunners,
635327
635369
  setConfig: () => setConfig,
635328
635370
  verifyAppDrivers: () => verifyAppDrivers
@@ -635533,13 +635575,62 @@ function clearAppCache(config) {
635533
635575
  }
635534
635576
  cachedAppsByDir.delete(cacheKeyFor(config));
635535
635577
  }
635578
+ async function patchAppCache(config, installed, deps = {}) {
635579
+ const key = cacheKeyFor(config);
635580
+ try {
635581
+ const verify = deps.verify ?? verifyDriverBinary;
635582
+ const drivers = (deps.detectDrivers ?? detectInstalledBrowserDrivers)(config);
635583
+ const descriptors = [];
635584
+ for (const b of installed ?? []) {
635585
+ if (b.name === "chrome" && b.path && b.driverPath && drivers.chromium) {
635586
+ descriptors.push({
635587
+ app: {
635588
+ name: "chrome",
635589
+ version: b.version,
635590
+ path: b.path,
635591
+ driver: b.driverPath
635592
+ },
635593
+ driverName: "chromedriver",
635594
+ driverPath: b.driverPath
635595
+ });
635596
+ } else if (b.name === "firefox" && b.path && drivers.gecko) {
635597
+ descriptors.push({
635598
+ app: { name: "firefox", version: b.version, path: b.path },
635599
+ driverName: b.driverPath ? "geckodriver" : void 0,
635600
+ driverPath: b.driverPath
635601
+ });
635602
+ }
635603
+ }
635604
+ if (descriptors.length === 0) {
635605
+ cachedAppsByDir.delete(key);
635606
+ return;
635607
+ }
635608
+ const verifiedApps = await verifyAppDrivers(descriptors, {
635609
+ verify: (driverName, driverPath) => verify(driverName, driverPath),
635610
+ logger: (msg, level) => log(config, level ?? "warning", msg)
635611
+ });
635612
+ const existing = cachedAppsByDir.get(key) ?? [];
635613
+ const existingNames = new Set(existing.map((a) => a?.name));
635614
+ const additions = verifiedApps.filter((a) => !existingNames.has(a?.name));
635615
+ cachedAppsByDir.set(key, [...existing, ...additions]);
635616
+ } catch {
635617
+ cachedAppsByDir.delete(key);
635618
+ }
635619
+ }
635620
+ function detectInstalledBrowserDrivers(config, isInstalled = (name) => Boolean(resolveHeavyDepPath(name, { cacheDir: config?.cacheDir }))) {
635621
+ return {
635622
+ chromium: isInstalled("appium-chromium-driver"),
635623
+ gecko: isInstalled("appium-geckodriver"),
635624
+ safari: isInstalled("appium-safari-driver")
635625
+ };
635626
+ }
635536
635627
  async function probeBrowserEnvironment({ config, browsersDir }) {
635537
635628
  setAppiumHome({ cacheDir: config?.cacheDir });
635538
635629
  const cwd = process.cwd();
635539
635630
  process.chdir(import_node_path9.default.join(__dirname4, "../.."));
635540
635631
  let installedBrowsers = [];
635541
635632
  let browserDetectionFailed = false;
635542
- let appiumDriverOutput = "";
635633
+ let installedDrivers = { chromium: false, gecko: false, safari: false };
635543
635634
  try {
635544
635635
  const browsersInstalled = resolveHeavyDepPath("@puppeteer/browsers", {
635545
635636
  cacheDir: config?.cacheDir
@@ -635559,47 +635650,11 @@ async function probeBrowserEnvironment({ config, browsersDir }) {
635559
635650
  installedBrowsers = [];
635560
635651
  }
635561
635652
  }
635562
- const appiumEntry = resolveHeavyDepPath("appium", {
635563
- cacheDir: config?.cacheDir
635564
- });
635565
- const installedAppiumDrivers = await new Promise((resolve) => {
635566
- if (!appiumEntry) {
635567
- resolve({
635568
- stdout: "",
635569
- stderr: "appium is not installed; driver list unavailable",
635570
- exitCode: 1
635571
- });
635572
- return;
635573
- }
635574
- const child = (0, import_node_child_process6.spawn)(process.execPath, [appiumEntry, "driver", "list"], { env: process.env });
635575
- let stdout = "";
635576
- let stderr = "";
635577
- child.stdout?.on("data", (c) => {
635578
- stdout += typeof c === "string" ? c : c.toString("utf8");
635579
- });
635580
- child.stderr?.on("data", (c) => {
635581
- stderr += typeof c === "string" ? c : c.toString("utf8");
635582
- });
635583
- child.on("error", (err) => {
635584
- resolve({
635585
- stdout: stdout.replace(/\n$/, ""),
635586
- stderr: (stderr + String(err)).replace(/\n$/, ""),
635587
- exitCode: 1
635588
- });
635589
- });
635590
- child.on("close", (code) => {
635591
- resolve({
635592
- stdout: stdout.replace(/\n$/, ""),
635593
- stderr: stderr.replace(/\n$/, ""),
635594
- exitCode: code ?? 1
635595
- });
635596
- });
635597
- });
635598
- appiumDriverOutput = installedAppiumDrivers.stdout + "\n" + installedAppiumDrivers.stderr;
635653
+ installedDrivers = detectInstalledBrowserDrivers(config);
635599
635654
  } finally {
635600
635655
  process.chdir(cwd);
635601
635656
  }
635602
- return { installedBrowsers, appiumDriverOutput, browserDetectionFailed };
635657
+ return { installedBrowsers, installedDrivers, browserDetectionFailed };
635603
635658
  }
635604
635659
  async function getAvailableApps({ config }) {
635605
635660
  const browsersDir = getBrowsersDir({ cacheDir: config?.cacheDir });
@@ -635607,12 +635662,12 @@ async function getAvailableApps({ config }) {
635607
635662
  const hit = cachedAppsByDir.get(key);
635608
635663
  if (hit)
635609
635664
  return hit;
635610
- const { installedBrowsers, appiumDriverOutput, browserDetectionFailed } = await probeBrowserEnvironment({ config, browsersDir });
635665
+ const { installedBrowsers, installedDrivers, browserDetectionFailed } = await probeBrowserEnvironment({ config, browsersDir });
635611
635666
  const descriptors = [];
635612
635667
  const chrome = installedBrowsers.find((browser) => browser.browser === "chrome");
635613
635668
  const chromeVersion = chrome?.buildId;
635614
635669
  const chromedriver = installedBrowsers.find((browser) => browser.browser === "chromedriver");
635615
- const appiumChromium = appiumDriverOutput.match(/\n.*chromium.*installed \(npm\).*\n/);
635670
+ const appiumChromium = installedDrivers.chromium;
635616
635671
  if (chrome && chromedriver && appiumChromium) {
635617
635672
  descriptors.push({
635618
635673
  app: {
@@ -635626,7 +635681,7 @@ async function getAvailableApps({ config }) {
635626
635681
  });
635627
635682
  }
635628
635683
  const firefox = installedBrowsers.find((browser) => browser.browser === "firefox");
635629
- const appiumFirefox = appiumDriverOutput.match(/\n.*gecko.*installed \(npm\).*\n/);
635684
+ const appiumFirefox = installedDrivers.gecko;
635630
635685
  if (firefox && appiumFirefox) {
635631
635686
  const geckodriverPath = resolveGeckodriverBinaryPath(config, browsersDir);
635632
635687
  descriptors.push({
@@ -635642,7 +635697,7 @@ async function getAvailableApps({ config }) {
635642
635697
  const currentPlatform = config?.environment?.platform ?? getEnvironment().platform;
635643
635698
  if (currentPlatform === "mac") {
635644
635699
  const safariVersion = await spawnCommand("defaults read /Applications/Safari.app/Contents/Info.plist CFBundleShortVersionString");
635645
- const appiumSafari = appiumDriverOutput.match(/\n.*safari.*installed \(npm\).*\n/);
635700
+ const appiumSafari = installedDrivers.safari;
635646
635701
  if (safariVersion.exitCode === 0 && appiumSafari) {
635647
635702
  descriptors.push({
635648
635703
  app: { name: "safari", version: safariVersion.stdout.trim(), path: "" },
@@ -638294,6 +638349,7 @@ async function loadHerettoContent(herettoConfig, log3, config, deps) {
638294
638349
  }
638295
638350
 
638296
638351
  // dist/core/detectTests.js
638352
+ var DETECTION_PARSE_CONCURRENCY = 8;
638297
638353
  async function detectTests2({ config }) {
638298
638354
  const files = await qualifyFiles({ config });
638299
638355
  log(config, "debug", `FILES:`);
@@ -638325,7 +638381,20 @@ async function isValidSourceFile({ config, files, source, allowedExtensions }) {
638325
638381
  if (files.indexOf(source) >= 0)
638326
638382
  return false;
638327
638383
  if (import_node_path11.default.extname(source) === ".json" || import_node_path11.default.extname(source) === ".yaml" || import_node_path11.default.extname(source) === ".yml") {
638328
- const content = await readFile({ fileURLOrPath: source });
638384
+ const ext = import_node_path11.default.extname(source).slice(1).toLowerCase();
638385
+ let rawContent;
638386
+ try {
638387
+ rawContent = await import_node_fs13.default.promises.readFile(source, "utf8");
638388
+ } catch {
638389
+ log(config, "debug", `${source} couldn't be read. Skipping.`);
638390
+ return false;
638391
+ }
638392
+ let content;
638393
+ try {
638394
+ content = ext === "json" ? JSON.parse(rawContent) : import_yaml3.default.parse(rawContent);
638395
+ } catch {
638396
+ content = rawContent;
638397
+ }
638329
638398
  if (typeof content !== "object") {
638330
638399
  log(config, "debug", `${source} isn't a valid test specification. Skipping.`);
638331
638400
  return false;
@@ -638366,6 +638435,7 @@ async function isValidSourceFile({ config, files, source, allowedExtensions }) {
638366
638435
  }
638367
638436
  }
638368
638437
  }
638438
+ config._parsedFileCache?.set(source, { rawContent, content });
638369
638439
  }
638370
638440
  const extension = import_node_path11.default.extname(source).substring(1);
638371
638441
  if (!allowed.has(extension)) {
@@ -638408,6 +638478,8 @@ async function qualifyFiles({ config }) {
638408
638478
  let sequence = [];
638409
638479
  const phaseByFile = /* @__PURE__ */ new Map();
638410
638480
  config._phaseByFile = phaseByFile;
638481
+ const parsedFileCache = /* @__PURE__ */ new Map();
638482
+ config._parsedFileCache = parsedFileCache;
638411
638483
  const toEntries = (value, phase) => (value == null ? [] : [].concat(value)).map((source) => ({
638412
638484
  source,
638413
638485
  phase
@@ -638536,27 +638608,34 @@ async function qualifyFiles({ config }) {
638536
638608
  return files;
638537
638609
  }
638538
638610
  async function parseTests({ config, files }) {
638539
- let specs = [];
638540
- for (const file of files) {
638611
+ const parseOneFile = async (file) => {
638541
638612
  log(config, "debug", `file: ${file}`);
638542
638613
  const extension = import_node_path11.default.extname(file).slice(1);
638543
638614
  let content = "";
638544
638615
  let rawContent;
638545
638616
  if (extension === "json" || extension === "yaml" || extension === "yml") {
638546
- try {
638547
- rawContent = await import_node_fs13.default.promises.readFile(file, "utf8");
638548
- if (extension === "json") {
638549
- content = JSON.parse(rawContent);
638550
- } else {
638551
- content = import_yaml3.default.parse(rawContent);
638617
+ const cached = config._parsedFileCache?.get(file);
638618
+ if (cached) {
638619
+ rawContent = cached.rawContent;
638620
+ content = cached.content;
638621
+ } else {
638622
+ try {
638623
+ rawContent = await import_node_fs13.default.promises.readFile(file, "utf8");
638624
+ if (extension === "json") {
638625
+ content = JSON.parse(rawContent);
638626
+ } else {
638627
+ content = import_yaml3.default.parse(rawContent);
638628
+ }
638629
+ } catch (err) {
638630
+ console.warn(`Failed to read/parse ${file}: ${err.message}`);
638631
+ content = await readFile({ fileURLOrPath: file });
638552
638632
  }
638553
- } catch (err) {
638554
- console.warn(`Failed to read/parse ${file}: ${err.message}`);
638555
- content = await readFile({ fileURLOrPath: file });
638556
638633
  }
638557
638634
  } else {
638558
638635
  content = await readFile({ fileURLOrPath: file });
638559
638636
  }
638637
+ if (content === null)
638638
+ return null;
638560
638639
  if (typeof content === "object") {
638561
638640
  if (!content.specId)
638562
638641
  content.specId = generateSpecId(file);
@@ -638620,7 +638699,10 @@ async function parseTests({ config, files }) {
638620
638699
  content = await resolvePaths({
638621
638700
  config,
638622
638701
  object: content,
638623
- filePath: file
638702
+ filePath: file,
638703
+ // Detection only ever resolves specs; passing the known objectType skips
638704
+ // resolvePaths' config_v3-then-spec_v3 discovery probe (item 3.1).
638705
+ objectType: "spec"
638624
638706
  });
638625
638707
  for (let t = 0; t < content.tests.length; t++) {
638626
638708
  const test = content.tests[t];
@@ -638685,13 +638767,16 @@ async function parseTests({ config, files }) {
638685
638767
  if (!validation.valid) {
638686
638768
  log(config, "warning", validation);
638687
638769
  log(config, "warning", `After applying setup and cleanup steps, ${file} isn't a valid test specification. Skipping.`);
638688
- continue;
638770
+ return null;
638689
638771
  }
638690
638772
  content = validation.object;
638691
638773
  content = await resolvePaths({
638692
638774
  config,
638693
638775
  object: content,
638694
- filePath: file
638776
+ filePath: file,
638777
+ // Detection only ever resolves specs; passing the known objectType skips
638778
+ // resolvePaths' config_v3-then-spec_v3 discovery probe (item 3.1).
638779
+ objectType: "spec"
638695
638780
  });
638696
638781
  content._phase = specPhase;
638697
638782
  for (const [testIdx, testMap] of stepLocations) {
@@ -638724,7 +638809,7 @@ async function parseTests({ config, files }) {
638724
638809
  }
638725
638810
  }
638726
638811
  }
638727
- specs.push(content);
638812
+ return content;
638728
638813
  } else {
638729
638814
  let id = generateSpecId(file);
638730
638815
  let spec = {
@@ -638752,7 +638837,7 @@ async function parseTests({ config, files }) {
638752
638837
  });
638753
638838
  if (!validation2.valid) {
638754
638839
  log(config, "warning", `Failed to convert ${file} to a runShell step: ${validation2.errors}. Skipping.`);
638755
- continue;
638840
+ return null;
638756
638841
  }
638757
638842
  spec.tests.push(test);
638758
638843
  const specValidation = validate({
@@ -638766,12 +638851,14 @@ async function parseTests({ config, files }) {
638766
638851
  spec = await resolvePaths({
638767
638852
  config,
638768
638853
  object: spec,
638769
- filePath: file
638854
+ filePath: file,
638855
+ // Detection only resolves specs — skip the discovery probe (3.1).
638856
+ objectType: "spec"
638770
638857
  });
638771
638858
  spec._phase = config._phaseByFile?.get(import_node_path11.default.resolve(file)) ?? "main";
638772
- specs.push(spec);
638859
+ return spec;
638773
638860
  }
638774
- continue;
638861
+ return null;
638775
638862
  }
638776
638863
  const tests = await detectTests({
638777
638864
  config,
@@ -638793,14 +638880,21 @@ async function parseTests({ config, files }) {
638793
638880
  spec = await resolvePaths({
638794
638881
  config,
638795
638882
  object: spec,
638796
- filePath: file
638883
+ filePath: file,
638884
+ // Detection only resolves specs — skip the discovery probe (3.1).
638885
+ objectType: "spec"
638797
638886
  });
638798
638887
  spec._phase = config._phaseByFile?.get(import_node_path11.default.resolve(file)) ?? "main";
638799
- specs.push(spec);
638888
+ return spec;
638800
638889
  }
638801
638890
  }
638802
- }
638803
- return specs;
638891
+ return null;
638892
+ };
638893
+ const slots = new Array(files.length).fill(null);
638894
+ await runConcurrent(files.map((_, i) => i), DETECTION_PARSE_CONCURRENCY, async (i) => {
638895
+ slots[i] = await parseOneFile(files[i]);
638896
+ });
638897
+ return slots.filter((spec) => spec != null);
638804
638898
  }
638805
638899
 
638806
638900
  // dist/core/resolveTests.js
@@ -638897,11 +638991,22 @@ ${JSON.stringify(documentArray, null, 2)}`);
638897
638991
  const openApiDocuments = [];
638898
638992
  if (config?.integrations?.openApi?.length > 0)
638899
638993
  openApiDocuments.push(...config.integrations.openApi);
638994
+ const descriptionCache = config._openApiDescriptionCache || (config._openApiDescriptionCache = /* @__PURE__ */ new Map());
638900
638995
  if (documentArray?.length > 0) {
638901
638996
  for (const definition of documentArray) {
638902
638997
  try {
638903
- const openApiDefinition = await loadDescription(definition.descriptionPath);
638904
- definition.definition = openApiDefinition;
638998
+ if (!definition.definition) {
638999
+ const key = definition.descriptionPath;
639000
+ let pending = descriptionCache.get(key);
639001
+ if (!pending) {
639002
+ pending = loadDescription(key).catch((err) => {
639003
+ descriptionCache.delete(key);
639004
+ throw err;
639005
+ });
639006
+ descriptionCache.set(key, pending);
639007
+ }
639008
+ definition.definition = await pending;
639009
+ }
638905
639010
  } catch (error) {
638906
639011
  log(config, "error", `Failed to load OpenAPI definition from ${definition.descriptionPath}: ${error.message}`);
638907
639012
  continue;
@@ -639028,6 +639133,7 @@ ${JSON.stringify(detectedTests, null, 2)}`);
639028
639133
  config,
639029
639134
  specs: []
639030
639135
  };
639136
+ config._openApiDescriptionCache = /* @__PURE__ */ new Map();
639031
639137
  log(config, "info", "Resolving test specs.");
639032
639138
  for (const spec of detectedTests) {
639033
639139
  const resolvedSpec = await resolveSpec({ config, spec });
@@ -642697,7 +642803,7 @@ async function setElementOutputs({ element }) {
642697
642803
  }
642698
642804
  async function findElementByRegex({ pattern, timeout, driver }) {
642699
642805
  await driver.pause(timeout);
642700
- const elements = await driver.$$("//*[normalize-space(text())]");
642806
+ const elements = await driver.$$("//*[text()[normalize-space()]]");
642701
642807
  for (const element of elements) {
642702
642808
  const text = await element.getText();
642703
642809
  if (text.match(pattern)) {
@@ -642784,7 +642890,8 @@ async function findElementByShorthand({ string, timeout = 5e3, driver }) {
642784
642890
  await el.waitForExist({ timeout });
642785
642891
  return el;
642786
642892
  }).catch(() => null);
642787
- const textPromise = driver.$(`//*[normalize-space(text())="${string}"]`).then(async (el) => {
642893
+ const textLiteral = xpathLiteral2(normalizeText(string));
642894
+ const textPromise = driver.$(`//*[normalize-space(.)=${textLiteral} and not(.//*[normalize-space(.)=${textLiteral}])]`).then(async (el) => {
642788
642895
  await el.waitForExist({ timeout });
642789
642896
  return el;
642790
642897
  }).catch(() => null);
@@ -642832,6 +642939,24 @@ async function findElementByShorthand({ string, timeout = 5e3, driver }) {
642832
642939
  function isRegexPattern(str) {
642833
642940
  return typeof str === "string" && str.startsWith("/") && str.endsWith("/");
642834
642941
  }
642942
+ function normalizeText(value) {
642943
+ return String(value ?? "").replace(/\s+/g, " ").trim();
642944
+ }
642945
+ function xpathLiteral2(value) {
642946
+ if (!value.includes('"'))
642947
+ return `"${value}"`;
642948
+ if (!value.includes("'"))
642949
+ return `'${value}'`;
642950
+ const parts = value.split('"').map((p) => `"${p}"`);
642951
+ return `concat(${parts.join(`, '"', `)})`;
642952
+ }
642953
+ function matchesText(value, pattern) {
642954
+ if (isRegexPattern(pattern)) {
642955
+ const regex = new RegExp(pattern.slice(1, -1));
642956
+ return regex.test(String(value ?? ""));
642957
+ }
642958
+ return normalizeText(value) === normalizeText(pattern);
642959
+ }
642835
642960
  function matchesPattern(value, pattern) {
642836
642961
  if (isRegexPattern(pattern)) {
642837
642962
  const regex = new RegExp(pattern.slice(1, -1));
@@ -642933,8 +643058,11 @@ async function findElementByCriteria({ selector, elementText, elementId, element
642933
643058
  }
642934
643059
  }
642935
643060
  }
642936
- if (elementText) {
642937
- xpathConditions.push(`normalize-space(text())`);
643061
+ if (elementText && !isRegexPattern(elementText)) {
643062
+ const lit = xpathLiteral2(normalizeText(elementText));
643063
+ xpathConditions.push(`(normalize-space(.)=${lit} and not(.//*[normalize-space(.)=${lit}]))`);
643064
+ } else if (elementText) {
643065
+ xpathConditions.push(`text()[normalize-space()]`);
642938
643066
  }
642939
643067
  let xpath;
642940
643068
  if (xpathConditions.length > 0) {
@@ -643011,7 +643139,9 @@ async function findElementByCriteria({ selector, elementText, elementId, element
643011
643139
  }
643012
643140
  elementCriteriaUsed.push(checkType.type);
643013
643141
  } else {
643014
- if (!actualValue || !matchesPattern(actualValue, checkType.value)) {
643142
+ const isTextCriterion = checkType.type === "elementText" || checkType.type === "elementAria";
643143
+ const matched = isTextCriterion ? matchesText(actualValue, checkType.value) : matchesPattern(actualValue, checkType.value);
643144
+ if (!actualValue || !matched) {
643015
643145
  allChecksPassed = false;
643016
643146
  break;
643017
643147
  }
@@ -646254,7 +646384,6 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646254
646384
  return result;
646255
646385
  } else {
646256
646386
  existFilePath = filePath;
646257
- filePath = import_node_path17.default.join(dir, `${step.stepId}_${Date.now()}.png`);
646258
646387
  }
646259
646388
  }
646260
646389
  }
@@ -646340,6 +646469,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646340
646469
  await driver.pause(100);
646341
646470
  }
646342
646471
  const recordingActive = !isAppCapture && isRecordingActive(driver);
646472
+ let captureBuffer;
646473
+ let appScratchPath;
646343
646474
  try {
646344
646475
  if (recordingActive) {
646345
646476
  await driver.execute(() => {
@@ -646349,18 +646480,24 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646349
646480
  });
646350
646481
  }
646351
646482
  if (isAppCapture) {
646352
- await appWindowScreenshot(appEntry, appWindowTarget, filePath);
646483
+ appScratchPath = import_node_path17.default.join(dir, `.appcapture_${step.stepId || "screenshot"}_${Date.now()}.png`);
646484
+ await appWindowScreenshot(appEntry, appWindowTarget, appScratchPath);
646485
+ captureBuffer = import_node_fs20.default.readFileSync(appScratchPath);
646353
646486
  } else {
646354
- await captureDriver.saveScreenshot(filePath);
646487
+ const base64 = await captureDriver.takeScreenshot();
646488
+ captureBuffer = Buffer.from(base64, "base64");
646355
646489
  }
646356
646490
  } catch (error) {
646357
646491
  result.status = "FAIL";
646358
646492
  result.description = `Couldn't save screenshot. ${error}`;
646359
- if (existFilePath && filePath !== existFilePath && import_node_fs20.default.existsSync(filePath)) {
646360
- import_node_fs20.default.unlinkSync(filePath);
646361
- }
646362
646493
  return result;
646363
646494
  } finally {
646495
+ if (appScratchPath && import_node_fs20.default.existsSync(appScratchPath)) {
646496
+ try {
646497
+ import_node_fs20.default.unlinkSync(appScratchPath);
646498
+ } catch {
646499
+ }
646500
+ }
646364
646501
  if (recordingActive) {
646365
646502
  try {
646366
646503
  await driver.execute(() => {
@@ -646372,6 +646509,7 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646372
646509
  }
646373
646510
  }
646374
646511
  }
646512
+ let finalBuffer = captureBuffer;
646375
646513
  if (step.screenshot.crop) {
646376
646514
  let padding = { top: 0, right: 0, bottom: 0, left: 0 };
646377
646515
  if (typeof step.screenshot.crop.padding === "number") {
@@ -646405,35 +646543,45 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646405
646543
  rect.y = Math.round(rect.y);
646406
646544
  rect.width = Math.round(rect.width);
646407
646545
  rect.height = Math.round(rect.height);
646408
- const imgMeta = await sharp(filePath).metadata();
646546
+ const imgMeta = await sharp(captureBuffer).metadata();
646409
646547
  const clamped = clampCropRect(rect, imgMeta.width, imgMeta.height);
646410
646548
  rect.x = clamped.x;
646411
646549
  rect.y = clamped.y;
646412
646550
  rect.width = clamped.width;
646413
646551
  rect.height = clamped.height;
646414
646552
  log(config, "debug", { padded_rect: rect });
646415
- const croppedPath = import_node_path17.default.join(dir, `cropped_${step.stepId || Date.now()}.png`);
646416
646553
  try {
646417
- await sharp(filePath).extract({
646554
+ finalBuffer = await sharp(captureBuffer).extract({
646418
646555
  left: rect.x,
646419
646556
  top: rect.y,
646420
646557
  width: rect.width,
646421
646558
  height: rect.height
646422
- }).toFile(croppedPath);
646423
- import_node_fs20.default.renameSync(croppedPath, filePath);
646559
+ }).png().toBuffer();
646424
646560
  } catch (error) {
646425
646561
  result.status = "FAIL";
646426
646562
  result.description = `Couldn't crop image. ${error}`;
646427
- if (existFilePath && filePath !== existFilePath && import_node_fs20.default.existsSync(filePath)) {
646428
- import_node_fs20.default.unlinkSync(filePath);
646429
- }
646430
646563
  return result;
646431
646564
  }
646432
646565
  }
646566
+ const writeFinalPng = (destination) => {
646567
+ try {
646568
+ import_node_fs20.default.writeFileSync(destination, finalBuffer);
646569
+ return true;
646570
+ } catch (error) {
646571
+ result.status = "FAIL";
646572
+ result.description = `Couldn't save screenshot. ${error}`;
646573
+ return false;
646574
+ }
646575
+ };
646576
+ if (isUrlPath) {
646577
+ if (!writeFinalPng(filePath))
646578
+ return result;
646579
+ }
646433
646580
  if (existFilePath) {
646434
646581
  if (step.screenshot.overwrite == "true" && !isUrlPath) {
646582
+ if (!writeFinalPng(existFilePath))
646583
+ return result;
646435
646584
  result.description += ` Overwrote existing file.`;
646436
- import_node_fs20.default.renameSync(filePath, existFilePath);
646437
646585
  result.outputs.screenshotPath = existFilePath;
646438
646586
  result.outputs.changed = true;
646439
646587
  if (step.screenshot.sourceIntegration) {
@@ -646447,13 +646595,10 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646447
646595
  let img2;
646448
646596
  try {
646449
646597
  img1 = PNG.sync.read(import_node_fs20.default.readFileSync(existFilePath));
646450
- img2 = PNG.sync.read(import_node_fs20.default.readFileSync(filePath));
646598
+ img2 = PNG.sync.read(finalBuffer);
646451
646599
  } catch (error) {
646452
646600
  result.status = "FAIL";
646453
646601
  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}`;
646454
- if (!isUrlPath && filePath !== existFilePath && import_node_fs20.default.existsSync(filePath)) {
646455
- import_node_fs20.default.unlinkSync(filePath);
646456
- }
646457
646602
  return result;
646458
646603
  }
646459
646604
  const aspectRatioMatch = aspectRatiosMatch(img1, img2);
@@ -646464,9 +646609,6 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646464
646609
  });
646465
646610
  if (!aspectRatioMatch) {
646466
646611
  result.description = `Couldn't compare images. Images have different aspect ratios.`;
646467
- if (!isUrlPath && filePath !== existFilePath && import_node_fs20.default.existsSync(filePath)) {
646468
- import_node_fs20.default.unlinkSync(filePath);
646469
- }
646470
646612
  return await evaluateApplicable();
646471
646613
  }
646472
646614
  if (img1.width !== img2.width || img1.height !== img2.height) {
@@ -646474,14 +646616,12 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646474
646616
  const height2 = Math.min(img1.height, img2.height);
646475
646617
  const img1ResizedBuffer = await sharp(img1.data, {
646476
646618
  raw: { width: img1.width, height: img1.height, channels: 4 }
646477
- }).resize(width2, height2).png().toBuffer();
646619
+ }).resize(width2, height2).raw().toBuffer();
646478
646620
  const img2ResizedBuffer = await sharp(img2.data, {
646479
646621
  raw: { width: img2.width, height: img2.height, channels: 4 }
646480
- }).resize(width2, height2).png().toBuffer();
646481
- const resizedImg1 = PNG.sync.read(img1ResizedBuffer);
646482
- const resizedImg2 = PNG.sync.read(img2ResizedBuffer);
646483
- img1.data = resizedImg1.data;
646484
- img2.data = resizedImg2.data;
646622
+ }).resize(width2, height2).raw().toBuffer();
646623
+ img1.data = img1ResizedBuffer;
646624
+ img2.data = img2ResizedBuffer;
646485
646625
  img1.width = width2;
646486
646626
  img1.height = height2;
646487
646627
  }
@@ -646492,9 +646632,6 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646492
646632
  } catch (error) {
646493
646633
  result.status = "FAIL";
646494
646634
  result.description = `Couldn't load screenshot comparison dependency (pixelmatch). ${error?.message ?? error}`;
646495
- if (!isUrlPath && filePath !== existFilePath && import_node_fs20.default.existsSync(filePath)) {
646496
- import_node_fs20.default.unlinkSync(filePath);
646497
- }
646498
646635
  return result;
646499
646636
  }
646500
646637
  const numDiffPixels = pixelmatchFn(img1.data, img2.data, null, width, height, { threshold: 5e-4 });
@@ -646511,7 +646648,8 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646511
646648
  });
646512
646649
  if (fractionalDiff > step.screenshot.maxVariation) {
646513
646650
  if (step.screenshot.overwrite == "aboveVariation" && !isUrlPath) {
646514
- import_node_fs20.default.renameSync(filePath, existFilePath);
646651
+ if (!writeFinalPng(existFilePath))
646652
+ return result;
646515
646653
  }
646516
646654
  result.description += ` The difference between the existing screenshot and new screenshot (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.screenshot.maxVariation}).`;
646517
646655
  if (isUrlPath) {
@@ -646535,14 +646673,13 @@ async function saveScreenshot({ config, step, driver, appSession }) {
646535
646673
  if (step.screenshot.sourceIntegration) {
646536
646674
  result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
646537
646675
  }
646538
- if (step.screenshot.overwrite != "true") {
646539
- import_node_fs20.default.unlinkSync(filePath);
646540
- }
646541
646676
  }
646542
646677
  }
646543
646678
  }
646544
646679
  }
646545
646680
  if (!result.outputs.screenshotPath) {
646681
+ if (!writeFinalPng(filePath))
646682
+ return result;
646546
646683
  result.outputs.screenshotPath = filePath;
646547
646684
  result.outputs.changed = true;
646548
646685
  if (step.screenshot.sourceIntegration) {
@@ -652929,6 +653066,9 @@ async function runTests(config, options = {}) {
652929
653066
  return null;
652930
653067
  }
652931
653068
  }
653069
+ if (typeof options.updateJoin === "function") {
653070
+ await options.updateJoin();
653071
+ }
652932
653072
  if (config.dryRun) {
652933
653073
  console.log(JSON.stringify(resolvedTests, null, 2));
652934
653074
  cleanTemp();
@@ -652960,23 +653100,43 @@ async function runTests(config, options = {}) {
652960
653100
  }
652961
653101
  if (needs.browsers.size > 0) {
652962
653102
  try {
652963
- const { getAvailableApps: getAvailableApps2, clearAppCache: clearAppCache2 } = await Promise.resolve().then(() => (init_config(), config_exports));
653103
+ const { getAvailableApps: getAvailableApps2, patchAppCache: patchAppCache2 } = await Promise.resolve().then(() => (init_config(), config_exports));
652964
653104
  const { ensureBrowserInstalled: ensureBrowserInstalled2, requiredBrowserAssets: requiredBrowserAssets2 } = await Promise.resolve().then(() => (init_browsers(), browsers_exports));
652965
653105
  const available = await getAvailableApps2({ config });
652966
653106
  const availableNames = new Set(available.map((a) => a.name));
652967
- let installedAnything = false;
653107
+ const installedDescriptors = [];
652968
653108
  for (const browser of needs.browsers) {
652969
653109
  if (availableNames.has(browser))
652970
653110
  continue;
652971
653111
  const assets = requiredBrowserAssets2(browser);
653112
+ const assetResults = {};
652972
653113
  for (const asset of assets) {
652973
- await ensureBrowserInstalled2(asset, { ctx, deps: { logger: preflightLogger } });
653114
+ assetResults[asset] = await ensureBrowserInstalled2(asset, {
653115
+ ctx,
653116
+ deps: { logger: preflightLogger }
653117
+ });
652974
653118
  }
652975
- if (assets.length > 0)
652976
- installedAnything = true;
653119
+ if (assets.length === 0)
653120
+ continue;
653121
+ if (browser === "chrome") {
653122
+ installedDescriptors.push({
653123
+ name: "chrome",
653124
+ version: assetResults.chrome?.version,
653125
+ path: assetResults.chrome?.path,
653126
+ driverPath: assetResults.chromedriver?.path
653127
+ });
653128
+ } else if (browser === "firefox") {
653129
+ installedDescriptors.push({
653130
+ name: "firefox",
653131
+ version: assetResults.firefox?.version,
653132
+ path: assetResults.firefox?.path,
653133
+ driverPath: assetResults.geckodriver?.path
653134
+ });
653135
+ }
653136
+ }
653137
+ if (installedDescriptors.length > 0) {
653138
+ await patchAppCache2(config, installedDescriptors);
652977
653139
  }
652978
- if (installedAnything)
652979
- clearAppCache2(config);
652980
653140
  } catch (browserErr) {
652981
653141
  log(config, "debug", `Browser pre-flight check skipped: ${browserErr?.message ?? browserErr}`);
652982
653142
  }
@@ -653003,8 +653163,8 @@ async function runTests(config, options = {}) {
653003
653163
  } else {
653004
653164
  results = await runSpecs({ resolvedTests });
653005
653165
  }
653006
- log(config, "info", "RESULTS:");
653007
- log(config, "info", results);
653166
+ log(config, "debug", "RESULTS:");
653167
+ log(config, "debug", results);
653008
653168
  log(config, "info", "Cleaning up and finishing post-processing.");
653009
653169
  cleanTemp();
653010
653170
  sendTelemetry(config, "runTests", results);