@gtkx/testing 0.21.0 → 1.0.0-rc.2

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 (216) hide show
  1. package/README.md +137 -34
  2. package/dist/act.d.ts +19 -0
  3. package/dist/act.d.ts.map +1 -0
  4. package/dist/act.js +56 -0
  5. package/dist/act.js.map +1 -0
  6. package/dist/bound-queries.d.ts +33 -0
  7. package/dist/bound-queries.d.ts.map +1 -0
  8. package/dist/bound-queries.js +2 -0
  9. package/dist/bound-queries.js.map +1 -0
  10. package/dist/build-queries.d.ts +16 -0
  11. package/dist/build-queries.d.ts.map +1 -0
  12. package/dist/build-queries.js +115 -0
  13. package/dist/build-queries.js.map +1 -0
  14. package/dist/cleanup-registry.d.ts +5 -0
  15. package/dist/cleanup-registry.d.ts.map +1 -0
  16. package/dist/cleanup-registry.js +21 -0
  17. package/dist/cleanup-registry.js.map +1 -0
  18. package/dist/config.d.ts +20 -52
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +17 -46
  21. package/dist/config.js.map +1 -1
  22. package/dist/editable.d.ts +8 -0
  23. package/dist/editable.d.ts.map +1 -0
  24. package/dist/editable.js +26 -0
  25. package/dist/editable.js.map +1 -0
  26. package/dist/errors.d.ts +30 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +135 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/fire-event.d.ts +8 -25
  31. package/dist/fire-event.d.ts.map +1 -1
  32. package/dist/fire-event.js +11 -30
  33. package/dist/fire-event.js.map +1 -1
  34. package/dist/frame-sync.d.ts +4 -0
  35. package/dist/frame-sync.d.ts.map +1 -0
  36. package/dist/frame-sync.js +37 -0
  37. package/dist/frame-sync.js.map +1 -0
  38. package/dist/index.d.ts +17 -12
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +11 -7
  41. package/dist/index.js.map +1 -1
  42. package/dist/matchers.d.ts +59 -0
  43. package/dist/matchers.d.ts.map +1 -0
  44. package/dist/matchers.js +117 -0
  45. package/dist/matchers.js.map +1 -0
  46. package/dist/pretty-widget.d.ts +24 -26
  47. package/dist/pretty-widget.d.ts.map +1 -1
  48. package/dist/pretty-widget.js +111 -117
  49. package/dist/pretty-widget.js.map +1 -1
  50. package/dist/queries.d.ts +190 -180
  51. package/dist/queries.d.ts.map +1 -1
  52. package/dist/queries.js +291 -320
  53. package/dist/queries.js.map +1 -1
  54. package/dist/register-test-hooks.d.ts +2 -0
  55. package/dist/register-test-hooks.d.ts.map +1 -0
  56. package/dist/register-test-hooks.js +16 -0
  57. package/dist/register-test-hooks.js.map +1 -0
  58. package/dist/render-hook.d.ts +15 -35
  59. package/dist/render-hook.d.ts.map +1 -1
  60. package/dist/render-hook.js +9 -45
  61. package/dist/render-hook.js.map +1 -1
  62. package/dist/render.d.ts +13 -42
  63. package/dist/render.d.ts.map +1 -1
  64. package/dist/render.js +138 -121
  65. package/dist/render.js.map +1 -1
  66. package/dist/role-helpers.d.ts +19 -53
  67. package/dist/role-helpers.d.ts.map +1 -1
  68. package/dist/role-helpers.js +48 -63
  69. package/dist/role-helpers.js.map +1 -1
  70. package/dist/screen.d.ts +7 -63
  71. package/dist/screen.d.ts.map +1 -1
  72. package/dist/screen.js +24 -110
  73. package/dist/screen.js.map +1 -1
  74. package/dist/screenshot.d.ts +21 -23
  75. package/dist/screenshot.d.ts.map +1 -1
  76. package/dist/screenshot.js +98 -50
  77. package/dist/screenshot.js.map +1 -1
  78. package/dist/suggestions.d.ts +27 -0
  79. package/dist/suggestions.d.ts.map +1 -0
  80. package/dist/suggestions.js +61 -0
  81. package/dist/suggestions.js.map +1 -0
  82. package/dist/traversal.d.ts +12 -10
  83. package/dist/traversal.d.ts.map +1 -1
  84. package/dist/traversal.js +36 -15
  85. package/dist/traversal.js.map +1 -1
  86. package/dist/types.d.ts +115 -169
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js.map +1 -1
  89. package/dist/user-event/adjustment.d.ts +10 -0
  90. package/dist/user-event/adjustment.d.ts.map +1 -0
  91. package/dist/user-event/adjustment.js +35 -0
  92. package/dist/user-event/adjustment.js.map +1 -0
  93. package/dist/user-event/click.d.ts +8 -0
  94. package/dist/user-event/click.d.ts.map +1 -0
  95. package/dist/user-event/click.js +73 -0
  96. package/dist/user-event/click.js.map +1 -0
  97. package/dist/user-event/controller.d.ts +8 -0
  98. package/dist/user-event/controller.d.ts.map +1 -0
  99. package/dist/user-event/controller.js +31 -0
  100. package/dist/user-event/controller.js.map +1 -0
  101. package/dist/user-event/dispatch.d.ts +7 -0
  102. package/dist/user-event/dispatch.d.ts.map +1 -0
  103. package/dist/user-event/dispatch.js +11 -0
  104. package/dist/user-event/dispatch.js.map +1 -0
  105. package/dist/user-event/event-wrapper.d.ts +4 -0
  106. package/dist/user-event/event-wrapper.d.ts.map +1 -0
  107. package/dist/user-event/event-wrapper.js +59 -0
  108. package/dist/user-event/event-wrapper.js.map +1 -0
  109. package/dist/user-event/gesture.d.ts +35 -0
  110. package/dist/user-event/gesture.d.ts.map +1 -0
  111. package/dist/user-event/gesture.js +138 -0
  112. package/dist/user-event/gesture.js.map +1 -0
  113. package/dist/user-event/index.d.ts +51 -0
  114. package/dist/user-event/index.d.ts.map +1 -0
  115. package/dist/user-event/index.js +42 -0
  116. package/dist/user-event/index.js.map +1 -0
  117. package/dist/user-event/keyboard.d.ts +10 -0
  118. package/dist/user-event/keyboard.d.ts.map +1 -0
  119. package/dist/user-event/keyboard.js +204 -0
  120. package/dist/user-event/keyboard.js.map +1 -0
  121. package/dist/user-event/pointer.d.ts +10 -0
  122. package/dist/user-event/pointer.d.ts.map +1 -0
  123. package/dist/user-event/pointer.js +30 -0
  124. package/dist/user-event/pointer.js.map +1 -0
  125. package/dist/user-event/selection.d.ts +5 -0
  126. package/dist/user-event/selection.d.ts.map +1 -0
  127. package/dist/user-event/selection.js +105 -0
  128. package/dist/user-event/selection.js.map +1 -0
  129. package/dist/user-event/state.d.ts +7 -0
  130. package/dist/user-event/state.d.ts.map +1 -0
  131. package/dist/user-event/state.js +3 -0
  132. package/dist/user-event/state.js.map +1 -0
  133. package/dist/user-event/text.d.ts +18 -0
  134. package/dist/user-event/text.d.ts.map +1 -0
  135. package/dist/user-event/text.js +113 -0
  136. package/dist/user-event/text.js.map +1 -0
  137. package/dist/wait-for.d.ts +16 -36
  138. package/dist/wait-for.d.ts.map +1 -1
  139. package/dist/wait-for.js +74 -70
  140. package/dist/wait-for.js.map +1 -1
  141. package/dist/widget-accessible-properties.d.ts +34 -0
  142. package/dist/widget-accessible-properties.d.ts.map +1 -0
  143. package/dist/widget-accessible-properties.js +260 -0
  144. package/dist/widget-accessible-properties.js.map +1 -0
  145. package/dist/within.d.ts +10 -26
  146. package/dist/within.d.ts.map +1 -1
  147. package/dist/within.js +16 -25
  148. package/dist/within.js.map +1 -1
  149. package/package.json +23 -12
  150. package/src/act.ts +77 -0
  151. package/src/bound-queries.ts +37 -0
  152. package/src/build-queries.ts +226 -0
  153. package/src/cleanup-registry.ts +27 -0
  154. package/src/config.ts +35 -62
  155. package/src/editable.ts +37 -0
  156. package/src/errors.ts +191 -0
  157. package/src/fire-event.ts +13 -38
  158. package/src/frame-sync.ts +51 -0
  159. package/src/index.ts +58 -15
  160. package/src/matchers.ts +251 -0
  161. package/src/pretty-widget.ts +163 -130
  162. package/src/queries.ts +485 -396
  163. package/src/register-test-hooks.ts +19 -0
  164. package/src/render-hook.tsx +38 -45
  165. package/src/render.tsx +192 -155
  166. package/src/role-helpers.ts +65 -72
  167. package/src/screen.ts +26 -114
  168. package/src/screenshot.ts +128 -56
  169. package/src/suggestions.ts +106 -0
  170. package/src/traversal.ts +56 -22
  171. package/src/types.ts +144 -172
  172. package/src/user-event/adjustment.ts +62 -0
  173. package/src/user-event/click.ts +99 -0
  174. package/src/user-event/controller.ts +58 -0
  175. package/src/user-event/dispatch.ts +25 -0
  176. package/src/user-event/event-wrapper.ts +80 -0
  177. package/src/user-event/gesture.ts +239 -0
  178. package/src/user-event/index.ts +81 -0
  179. package/src/user-event/keyboard.ts +290 -0
  180. package/src/user-event/pointer.ts +46 -0
  181. package/src/user-event/selection.ts +159 -0
  182. package/src/user-event/state.ts +8 -0
  183. package/src/user-event/text.ts +172 -0
  184. package/src/wait-for.ts +117 -78
  185. package/src/widget-accessible-properties.ts +388 -0
  186. package/src/within.ts +29 -27
  187. package/dist/bind-queries.d.ts +0 -14
  188. package/dist/bind-queries.d.ts.map +0 -1
  189. package/dist/bind-queries.js +0 -29
  190. package/dist/bind-queries.js.map +0 -1
  191. package/dist/error-builder.d.ts +0 -28
  192. package/dist/error-builder.d.ts.map +0 -1
  193. package/dist/error-builder.js +0 -81
  194. package/dist/error-builder.js.map +0 -1
  195. package/dist/timing.d.ts +0 -18
  196. package/dist/timing.d.ts.map +0 -1
  197. package/dist/timing.js +0 -18
  198. package/dist/timing.js.map +0 -1
  199. package/dist/user-event.d.ts +0 -138
  200. package/dist/user-event.d.ts.map +0 -1
  201. package/dist/user-event.js +0 -411
  202. package/dist/user-event.js.map +0 -1
  203. package/dist/widget-text.d.ts +0 -69
  204. package/dist/widget-text.d.ts.map +0 -1
  205. package/dist/widget-text.js +0 -177
  206. package/dist/widget-text.js.map +0 -1
  207. package/dist/widget.d.ts +0 -3
  208. package/dist/widget.d.ts.map +0 -1
  209. package/dist/widget.js +0 -13
  210. package/dist/widget.js.map +0 -1
  211. package/src/bind-queries.ts +0 -52
  212. package/src/error-builder.ts +0 -102
  213. package/src/timing.ts +0 -17
  214. package/src/user-event.ts +0 -492
  215. package/src/widget-text.ts +0 -202
  216. package/src/widget.ts +0 -15
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,WAAW,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACR,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC7E,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EACH,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,uBAAuB,EACvB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,yBAAyB,EACzB,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,sBAAsB,EACtB,WAAW,EACX,WAAW,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1F,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACR,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,EACf,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,gBAAgB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACR,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,GACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,14 +1,18 @@
1
+ import "./register-test-hooks.js";
2
+ export { act } from "./act.js";
1
3
  export { configure, getConfig } from "./config.js";
2
4
  export { fireEvent } from "./fire-event.js";
3
- export { prettyWidget } from "./pretty-widget.js";
4
- export { findAllByLabelText, findAllByName, findAllByRole, findAllByText, findByLabelText, findByName, findByRole, findByText, queryAllByLabelText, queryAllByName, queryAllByRole, queryAllByText, queryByLabelText, queryByName, queryByRole, queryByText, } from "./queries.js";
5
- export { cleanup, render } from "./render.js";
5
+ export { matchers, registerMatchers } from "./matchers.js";
6
+ export { logWidget, prettyWidget } from "./pretty-widget.js";
7
+ export { findAllByDisplayValue, findAllByLabelText, findAllByName, findAllByPlaceholderText, findAllByRole, findAllByText, findByDisplayValue, findByLabelText, findByName, findByPlaceholderText, findByRole, findByText, getAllByDisplayValue, getAllByLabelText, getAllByName, getAllByPlaceholderText, getAllByRole, getAllByText, getByDisplayValue, getByLabelText, getByName, getByPlaceholderText, getByRole, getByText, getDefaultNormalizer, queryAllByDisplayValue, queryAllByLabelText, queryAllByName, queryAllByPlaceholderText, queryAllByRole, queryAllByText, queryByDisplayValue, queryByLabelText, queryByName, queryByPlaceholderText, queryByRole, queryByText, } from "./queries.js";
6
8
  export { renderHook } from "./render-hook.js";
7
- export { getRoles, logRoles, prettyRoles } from "./role-helpers.js";
9
+ export { cleanup, render } from "./render.js";
10
+ export { formatRole, getRoles, logRoles, prettyRoles } from "./role-helpers.js";
8
11
  export { screen } from "./screen.js";
9
- export { screenshot } from "./screenshot.js";
10
- export { tick } from "./timing.js";
11
- export { userEvent } from "./user-event.js";
12
+ export { captureAndSaveScreenshot, logScreenshotPath, screenshot } from "./screenshot.js";
13
+ export { getSuggestedQuery } from "./suggestions.js";
14
+ export { userEvent } from "./user-event/index.js";
12
15
  export { waitFor, waitForElementToBeRemoved } from "./wait-for.js";
16
+ export { getWidgetErrorMessage, getWidgetInvalidState, getWidgetNodeText, } from "./widget-accessible-properties.js";
13
17
  export { within } from "./within.js";
14
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,WAAW,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAkBnC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAG/B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EACH,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,uBAAuB,EACvB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,yBAAyB,EACzB,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,sBAAsB,EACtB,WAAW,EACX,WAAW,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA8BrD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,GACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC","sourcesContent":["import \"./register-test-hooks.js\";\n\nexport { act } from \"./act.js\";\nexport type { BoundQueries, RenderResult, Screen } from \"./bound-queries.js\";\nexport type { Config, ConfigFn } from \"./config.js\";\nexport { configure, getConfig } from \"./config.js\";\nexport { fireEvent } from \"./fire-event.js\";\nexport type { TextExpectation } from \"./matchers.js\";\nexport { matchers, registerMatchers } from \"./matchers.js\";\nexport type { PrettyWidgetOptions } from \"./pretty-widget.js\";\nexport { logWidget, prettyWidget } from \"./pretty-widget.js\";\nexport {\n findAllByDisplayValue,\n findAllByLabelText,\n findAllByName,\n findAllByPlaceholderText,\n findAllByRole,\n findAllByText,\n findByDisplayValue,\n findByLabelText,\n findByName,\n findByPlaceholderText,\n findByRole,\n findByText,\n getAllByDisplayValue,\n getAllByLabelText,\n getAllByName,\n getAllByPlaceholderText,\n getAllByRole,\n getAllByText,\n getByDisplayValue,\n getByLabelText,\n getByName,\n getByPlaceholderText,\n getByRole,\n getByText,\n getDefaultNormalizer,\n queryAllByDisplayValue,\n queryAllByLabelText,\n queryAllByName,\n queryAllByPlaceholderText,\n queryAllByRole,\n queryAllByText,\n queryByDisplayValue,\n queryByLabelText,\n queryByName,\n queryByPlaceholderText,\n queryByRole,\n queryByText,\n} from \"./queries.js\";\nexport { renderHook } from \"./render-hook.js\";\nexport { cleanup, render } from \"./render.js\";\nexport { formatRole, getRoles, logRoles, prettyRoles } from \"./role-helpers.js\";\nexport { screen } from \"./screen.js\";\nexport { captureAndSaveScreenshot, logScreenshotPath, screenshot } from \"./screenshot.js\";\nexport type { Method, Suggestion, Variant } from \"./suggestions.js\";\nexport { getSuggestedQuery } from \"./suggestions.js\";\nexport type { Container } from \"./traversal.js\";\nexport type {\n ByRoleOptions,\n ByRoleValue,\n Matcher,\n MatcherFunction,\n MatcherOptions,\n NormalizerFn,\n NormalizerOptions,\n RenderHookOptions,\n RenderHookResult,\n RenderOptions,\n ScreenshotOptions,\n ScreenshotResult,\n WaitForOptions,\n WindowSelector,\n WrapperComponent,\n} from \"./types.js\";\nexport type {\n DragOffset,\n DragOptions,\n DropContent,\n DropOptions,\n PointerInput,\n ScrollDelta,\n TabOptions,\n TypeOptions,\n UserEvent,\n} from \"./user-event/index.js\";\nexport { userEvent } from \"./user-event/index.js\";\nexport { waitFor, waitForElementToBeRemoved } from \"./wait-for.js\";\nexport {\n getWidgetErrorMessage,\n getWidgetInvalidState,\n getWidgetNodeText,\n} from \"./widget-accessible-properties.js\";\nexport { within } from \"./within.js\";\n"]}
@@ -0,0 +1,59 @@
1
+ /** The expected value for a text matcher: an exact string or a regular expression. */
2
+ type TextExpectation = string | RegExp;
3
+ type MatcherResult = {
4
+ pass: boolean;
5
+ message: () => string;
6
+ };
7
+ type TextMatcher = (received: unknown, expected?: TextExpectation) => MatcherResult;
8
+ type StateMatcher = (received: unknown) => MatcherResult;
9
+ type ValueMatcher = (received: unknown, expected: number) => MatcherResult;
10
+ type MatcherImplementations = {
11
+ toHaveDisplayValue: TextMatcher;
12
+ toHaveTextContent: TextMatcher;
13
+ toHaveAccessibleName: TextMatcher;
14
+ toHavePlaceholderText: TextMatcher;
15
+ toBeChecked: StateMatcher;
16
+ toBePressed: StateMatcher;
17
+ toBeExpanded: StateMatcher;
18
+ toBeSelected: StateMatcher;
19
+ toHaveValue: ValueMatcher;
20
+ };
21
+ declare const toHaveDisplayValue: TextMatcher;
22
+ declare const toHaveTextContent: TextMatcher;
23
+ declare const toHaveAccessibleName: TextMatcher;
24
+ declare const toHavePlaceholderText: TextMatcher;
25
+ declare const toBeChecked: StateMatcher;
26
+ declare const toBePressed: StateMatcher;
27
+ declare const toBeExpanded: StateMatcher;
28
+ declare const toBeSelected: StateMatcher;
29
+ /** The widget assertion matchers keyed by name, suitable for passing to `expect.extend`. */
30
+ declare const matchers: MatcherImplementations;
31
+ declare function toHaveValue(received: unknown, expected: number): MatcherResult;
32
+ /** Registers the widget matchers on the global `expect`, when one is available. Safe to call more than once. */
33
+ declare const registerMatchers: () => void;
34
+ declare module "@vitest/expect" {
35
+ interface Assertion {
36
+ toHaveDisplayValue(expected?: TextExpectation): void;
37
+ toHaveTextContent(expected?: TextExpectation): void;
38
+ toHaveAccessibleName(expected?: TextExpectation): void;
39
+ toHavePlaceholderText(expected?: TextExpectation): void;
40
+ toBeChecked(): void;
41
+ toBePressed(): void;
42
+ toBeExpanded(): void;
43
+ toBeSelected(): void;
44
+ toHaveValue(expected: number): void;
45
+ }
46
+ interface AsymmetricMatchersContaining {
47
+ toHaveDisplayValue(expected?: TextExpectation): void;
48
+ toHaveTextContent(expected?: TextExpectation): void;
49
+ toHaveAccessibleName(expected?: TextExpectation): void;
50
+ toHavePlaceholderText(expected?: TextExpectation): void;
51
+ toBeChecked(): void;
52
+ toBePressed(): void;
53
+ toBeExpanded(): void;
54
+ toBeSelected(): void;
55
+ toHaveValue(expected: number): void;
56
+ }
57
+ }
58
+ export { toHaveDisplayValue, toHaveTextContent, toHaveAccessibleName, toHavePlaceholderText, toBeChecked, toBePressed, toBeExpanded, toBeSelected, matchers, toHaveValue, registerMatchers, type TextExpectation, };
59
+ //# sourceMappingURL=matchers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../src/matchers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /// <reference types="@vitest/expect" />
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import { getWidgetAccessibleName, getWidgetCheckedState, getWidgetDisplayValue, getWidgetExpandedState, getWidgetNodeText, getWidgetPlaceholderText, getWidgetPressedState, getWidgetSelectedState, getWidgetValue, } from "./widget-accessible-properties.js";
4
+ const toHaveDisplayValue = textMatcher("toHaveDisplayValue", getWidgetDisplayValue, "exact");
5
+ const toHaveTextContent = textMatcher("toHaveTextContent", (widget) => getWidgetNodeText(widget) ?? getWidgetAccessibleName(widget), "substring");
6
+ const toHaveAccessibleName = textMatcher("toHaveAccessibleName", getWidgetAccessibleName, "exact");
7
+ const toHavePlaceholderText = textMatcher("toHavePlaceholderText", getWidgetPlaceholderText, "exact");
8
+ const toBeChecked = booleanStateMatcher("toBeChecked", "checked", getWidgetCheckedState);
9
+ const toBePressed = booleanStateMatcher("toBePressed", "pressed", getWidgetPressedState);
10
+ const toBeExpanded = booleanStateMatcher("toBeExpanded", "expanded", getWidgetExpandedState);
11
+ const toBeSelected = booleanStateMatcher("toBeSelected", "selected", getWidgetSelectedState);
12
+ /** The widget assertion matchers keyed by name, suitable for passing to `expect.extend`. */
13
+ const matchers = {
14
+ toHaveDisplayValue,
15
+ toHaveTextContent,
16
+ toHaveAccessibleName,
17
+ toHavePlaceholderText,
18
+ toBeChecked,
19
+ toBePressed,
20
+ toBeExpanded,
21
+ toBeSelected,
22
+ toHaveValue,
23
+ };
24
+ const registration = { isRegistered: false };
25
+ const asWidget = (received, matcherName) => {
26
+ if (!(received instanceof Gtk.Widget)) {
27
+ throw new TypeError(`${matcherName}: received value must be a Gtk.Widget, got ${typeof received}`);
28
+ }
29
+ return received;
30
+ };
31
+ const describeWidget = (widget) => {
32
+ const role = Gtk.AccessibleRole[widget.getAccessibleRole()];
33
+ const name = getWidgetAccessibleName(widget);
34
+ return name === null ? `<${role}>` : `<${role} name=${JSON.stringify(name)}>`;
35
+ };
36
+ const isTextMatch = (actual, expected, mode) => {
37
+ if (expected instanceof RegExp) {
38
+ expected.lastIndex = 0;
39
+ return expected.test(actual);
40
+ }
41
+ return mode === "exact" ? actual === expected : actual.includes(expected);
42
+ };
43
+ const describeExpected = (expected) => expected instanceof RegExp ? String(expected) : JSON.stringify(expected);
44
+ const negationPrefix = (isPass) => (isPass ? "not " : "");
45
+ const nonEmptyResult = ({ matcherName, widget, actual }) => {
46
+ const isPass = actual !== null && actual !== "";
47
+ return {
48
+ pass: isPass,
49
+ message: () => `expected widget ${negationPrefix(isPass)}to have a non-empty value for ${matcherName}, ` +
50
+ `but got ${JSON.stringify(actual)}\n${describeWidget(widget)}`,
51
+ };
52
+ };
53
+ const matchedResult = (context, expected, isPass) => ({
54
+ pass: isPass,
55
+ message: () => `expected widget ${negationPrefix(isPass)}${context.matcherName} ${describeExpected(expected)}, ` +
56
+ `but received ${JSON.stringify(context.actual)}\n${describeWidget(context.widget)}`,
57
+ });
58
+ function textMatcher(matcherName, read, mode) {
59
+ return (received, expected) => {
60
+ const widget = asWidget(received, matcherName);
61
+ const actual = read(widget);
62
+ const context = { matcherName, widget, actual };
63
+ if (expected === undefined) {
64
+ return nonEmptyResult(context);
65
+ }
66
+ return matchedResult(context, expected, actual !== null && isTextMatch(actual, expected, mode));
67
+ };
68
+ }
69
+ function booleanStateMatcher(matcherName, stateName, read) {
70
+ return (received) => {
71
+ const widget = asWidget(received, matcherName);
72
+ const state = read(widget);
73
+ if (state === null) {
74
+ throw new Error(`${matcherName}: widget does not expose a ${stateName} state ` +
75
+ `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\n${describeWidget(widget)}`);
76
+ }
77
+ return {
78
+ pass: state,
79
+ message: () => `expected widget ${negationPrefix(state)}to be ${stateName}\n${describeWidget(widget)}`,
80
+ };
81
+ };
82
+ }
83
+ function toHaveValue(received, expected) {
84
+ const widget = asWidget(received, "toHaveValue");
85
+ const actual = getWidgetValue(widget).now;
86
+ if (actual === null) {
87
+ throw new Error("toHaveValue: widget does not expose a numeric value " +
88
+ `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\n${describeWidget(widget)}`);
89
+ }
90
+ const isPass = actual === expected;
91
+ return {
92
+ pass: isPass,
93
+ message: () => `expected widget ${negationPrefix(isPass)}to have value ${String(expected)}, ` +
94
+ `but received ${String(actual)}\n${describeWidget(widget)}`,
95
+ };
96
+ }
97
+ const globalExpect = () => {
98
+ const candidate = Reflect.get(globalThis, "expect");
99
+ if (candidate && typeof candidate.extend === "function") {
100
+ return candidate;
101
+ }
102
+ return null;
103
+ };
104
+ /** Registers the widget matchers on the global `expect`, when one is available. Safe to call more than once. */
105
+ const registerMatchers = () => {
106
+ if (registration.isRegistered) {
107
+ return;
108
+ }
109
+ const expect = globalExpect();
110
+ if (!expect) {
111
+ return;
112
+ }
113
+ expect.extend(matchers);
114
+ registration.isRegistered = true;
115
+ };
116
+ export { toHaveDisplayValue, toHaveTextContent, toHaveAccessibleName, toHavePlaceholderText, toBeChecked, toBePressed, toBeExpanded, toBeSelected, matchers, toHaveValue, registerMatchers, };
117
+ //# sourceMappingURL=matchers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchers.js","sourceRoot":"","sources":["../src/matchers.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"@vitest/expect\" />\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport {\n getWidgetAccessibleName,\n getWidgetCheckedState,\n getWidgetDisplayValue,\n getWidgetExpandedState,\n getWidgetNodeText,\n getWidgetPlaceholderText,\n getWidgetPressedState,\n getWidgetSelectedState,\n getWidgetValue,\n} from \"./widget-accessible-properties.js\";\n\n/** The expected value for a text matcher: an exact string or a regular expression. */\ntype TextExpectation = string | RegExp;\ntype MatcherResult = { pass: boolean; message: () => string };\ntype TextMatcher = (received: unknown, expected?: TextExpectation) => MatcherResult;\ntype StateMatcher = (received: unknown) => MatcherResult;\ntype ValueMatcher = (received: unknown, expected: number) => MatcherResult;\ntype TextMatcherContext = { matcherName: string; widget: Gtk.Widget; actual: string | null };\n\ntype MatcherImplementations = {\n toHaveDisplayValue: TextMatcher;\n toHaveTextContent: TextMatcher;\n toHaveAccessibleName: TextMatcher;\n toHavePlaceholderText: TextMatcher;\n toBeChecked: StateMatcher;\n toBePressed: StateMatcher;\n toBeExpanded: StateMatcher;\n toBeSelected: StateMatcher;\n toHaveValue: ValueMatcher;\n};\n\ntype ExpectExtend = { extend: (m: MatcherImplementations) => void };\n\nconst toHaveDisplayValue: TextMatcher = textMatcher(\"toHaveDisplayValue\", getWidgetDisplayValue, \"exact\");\n\nconst toHaveTextContent: TextMatcher = textMatcher(\n \"toHaveTextContent\",\n (widget) => getWidgetNodeText(widget) ?? getWidgetAccessibleName(widget),\n \"substring\",\n);\n\nconst toHaveAccessibleName: TextMatcher = textMatcher(\"toHaveAccessibleName\", getWidgetAccessibleName, \"exact\");\n\nconst toHavePlaceholderText: TextMatcher = textMatcher(\n \"toHavePlaceholderText\",\n getWidgetPlaceholderText,\n \"exact\",\n);\n\nconst toBeChecked: StateMatcher = booleanStateMatcher(\"toBeChecked\", \"checked\", getWidgetCheckedState);\nconst toBePressed: StateMatcher = booleanStateMatcher(\"toBePressed\", \"pressed\", getWidgetPressedState);\nconst toBeExpanded: StateMatcher = booleanStateMatcher(\"toBeExpanded\", \"expanded\", getWidgetExpandedState);\nconst toBeSelected: StateMatcher = booleanStateMatcher(\"toBeSelected\", \"selected\", getWidgetSelectedState);\n\n/** The widget assertion matchers keyed by name, suitable for passing to `expect.extend`. */\nconst matchers: MatcherImplementations = {\n toHaveDisplayValue,\n toHaveTextContent,\n toHaveAccessibleName,\n toHavePlaceholderText,\n toBeChecked,\n toBePressed,\n toBeExpanded,\n toBeSelected,\n toHaveValue,\n};\n\nconst registration = { isRegistered: false };\n\nconst asWidget = (received: unknown, matcherName: string): Gtk.Widget => {\n if (!(received instanceof Gtk.Widget)) {\n throw new TypeError(`${matcherName}: received value must be a Gtk.Widget, got ${typeof received}`);\n }\n\n return received;\n};\n\nconst describeWidget = (widget: Gtk.Widget): string => {\n const role = Gtk.AccessibleRole[widget.getAccessibleRole()];\n const name = getWidgetAccessibleName(widget);\n\n return name === null ? `<${role}>` : `<${role} name=${JSON.stringify(name)}>`;\n};\n\nconst isTextMatch = (actual: string, expected: TextExpectation, mode: \"exact\" | \"substring\"): boolean => {\n if (expected instanceof RegExp) {\n expected.lastIndex = 0;\n\n return expected.test(actual);\n }\n\n return mode === \"exact\" ? actual === expected : actual.includes(expected);\n};\n\nconst describeExpected = (expected: TextExpectation): string =>\n expected instanceof RegExp ? String(expected) : JSON.stringify(expected);\n\nconst negationPrefix = (isPass: boolean): string => (isPass ? \"not \" : \"\");\n\nconst nonEmptyResult = ({ matcherName, widget, actual }: TextMatcherContext): MatcherResult => {\n const isPass = actual !== null && actual !== \"\";\n\n return {\n pass: isPass,\n message: () =>\n `expected widget ${negationPrefix(isPass)}to have a non-empty value for ${matcherName}, ` +\n `but got ${JSON.stringify(actual)}\\n${describeWidget(widget)}`,\n };\n};\n\nconst matchedResult = (context: TextMatcherContext, expected: TextExpectation, isPass: boolean): MatcherResult => ({\n pass: isPass,\n message: () =>\n `expected widget ${negationPrefix(isPass)}${context.matcherName} ${describeExpected(expected)}, ` +\n `but received ${JSON.stringify(context.actual)}\\n${describeWidget(context.widget)}`,\n});\n\nfunction textMatcher(\n matcherName: string,\n read: (widget: Gtk.Widget) => string | null,\n mode: \"exact\" | \"substring\",\n): TextMatcher {\n return (received: unknown, expected?: TextExpectation): MatcherResult => {\n const widget = asWidget(received, matcherName);\n const actual = read(widget);\n const context: TextMatcherContext = { matcherName, widget, actual };\n\n if (expected === undefined) {\n return nonEmptyResult(context);\n }\n\n return matchedResult(context, expected, actual !== null && isTextMatch(actual, expected, mode));\n };\n}\n\nfunction booleanStateMatcher(\n matcherName: string,\n stateName: string,\n read: (widget: Gtk.Widget) => boolean | null,\n): StateMatcher {\n return (received: unknown): MatcherResult => {\n const widget = asWidget(received, matcherName);\n const state = read(widget);\n\n if (state === null) {\n throw new Error(\n `${matcherName}: widget does not expose a ${stateName} state ` +\n `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\\n${describeWidget(widget)}`,\n );\n }\n\n return {\n pass: state,\n message: () =>\n `expected widget ${negationPrefix(state)}to be ${stateName}\\n${describeWidget(widget)}`,\n };\n };\n}\n\nfunction toHaveValue(received: unknown, expected: number): MatcherResult {\n const widget = asWidget(received, \"toHaveValue\");\n const actual = getWidgetValue(widget).now;\n\n if (actual === null) {\n throw new Error(\n \"toHaveValue: widget does not expose a numeric value \" +\n `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\\n${describeWidget(widget)}`,\n );\n }\n\n const isPass = actual === expected;\n\n return {\n pass: isPass,\n message: () =>\n `expected widget ${negationPrefix(isPass)}to have value ${String(expected)}, ` +\n `but received ${String(actual)}\\n${describeWidget(widget)}`,\n };\n}\n\nconst globalExpect = (): ExpectExtend | null => {\n const candidate: unknown = Reflect.get(globalThis, \"expect\");\n\n if (candidate && typeof (candidate as ExpectExtend).extend === \"function\") {\n return candidate as ExpectExtend;\n }\n\n return null;\n};\n\n/** Registers the widget matchers on the global `expect`, when one is available. Safe to call more than once. */\nconst registerMatchers = (): void => {\n if (registration.isRegistered) {\n return;\n }\n\n const expect = globalExpect();\n\n if (!expect) {\n return;\n }\n\n expect.extend(matchers);\n registration.isRegistered = true;\n};\n\ndeclare module \"@vitest/expect\" {\n /* eslint-disable @typescript-eslint/consistent-type-definitions -- declaration merging requires interfaces */\n interface Assertion {\n toHaveDisplayValue(expected?: TextExpectation): void;\n toHaveTextContent(expected?: TextExpectation): void;\n toHaveAccessibleName(expected?: TextExpectation): void;\n toHavePlaceholderText(expected?: TextExpectation): void;\n toBeChecked(): void;\n toBePressed(): void;\n toBeExpanded(): void;\n toBeSelected(): void;\n toHaveValue(expected: number): void;\n }\n\n interface AsymmetricMatchersContaining {\n toHaveDisplayValue(expected?: TextExpectation): void;\n toHaveTextContent(expected?: TextExpectation): void;\n toHaveAccessibleName(expected?: TextExpectation): void;\n toHavePlaceholderText(expected?: TextExpectation): void;\n toBeChecked(): void;\n toBePressed(): void;\n toBeExpanded(): void;\n toBeSelected(): void;\n toHaveValue(expected: number): void;\n }\n /* eslint-enable @typescript-eslint/consistent-type-definitions */\n}\n\nexport {\n toHaveDisplayValue,\n toHaveTextContent,\n toHaveAccessibleName,\n toHavePlaceholderText,\n toBeChecked,\n toBePressed,\n toBeExpanded,\n toBeSelected,\n matchers,\n toHaveValue,\n registerMatchers,\n type TextExpectation,\n};\n"]}
@@ -1,37 +1,35 @@
1
+ import type * as Gtk from "@gtkx/gi/gtk";
1
2
  import { type Container } from "./traversal.js";
3
+ type WidgetIdResolver = (widget: Gtk.Widget) => string;
2
4
  /**
3
- * Options for {@link prettyWidget}.
5
+ * Options controlling how a widget tree is rendered to a string by
6
+ * {@link prettyWidget} and {@link logWidget}.
4
7
  */
5
- export type PrettyWidgetOptions = {
6
- /** Maximum output length before truncation (default: 7000) */
8
+ type PrettyWidgetOptions = {
9
+ /** Truncates the output once it exceeds this many characters. */
7
10
  maxLength?: number;
8
- /** Enable ANSI color highlighting (default: auto-detect) */
11
+ /** Whether to apply ANSI color highlighting; defaults to the terminal capabilities. */
9
12
  highlight?: boolean;
10
- /** Include widget IDs for MCP/agentic interactions (default: false) */
11
- includeIds?: boolean;
13
+ /** Resolves an `id` attribute to show for each widget. */
14
+ getId?: WidgetIdResolver;
15
+ /** Stops descending past this depth, replacing deeper children with a summary line. */
16
+ maxDepth?: number;
12
17
  };
13
18
  /**
14
- * Formats a widget tree as a readable string for debugging.
19
+ * Renders a widget tree as an indented, HTML-like string showing each widget's
20
+ * tag, accessible attributes, and text content.
15
21
  *
16
- * Renders the widget hierarchy in an HTML-like format with accessibility
17
- * attributes like role, name, and text content.
18
- *
19
- * @param container - The container widget or application to format
20
- * @param options - Formatting options for length and highlighting
21
- * @returns Formatted string representation of the widget tree
22
- *
23
- * @example
24
- * ```tsx
25
- * import { prettyWidget } from "@gtkx/testing";
22
+ * @param container The scope whose widget tree is formatted.
23
+ * @param options Formatting options such as truncation length and highlighting.
24
+ * @returns The formatted representation of the tree.
25
+ */
26
+ declare const prettyWidget: (container: Container, options?: PrettyWidgetOptions) => string;
27
+ /**
28
+ * Prints one or more widget trees to the console using {@link prettyWidget}.
26
29
  *
27
- * console.log(prettyWidget(application));
28
- * // Output:
29
- * // <GtkApplicationWindow role="window">
30
- * // <GtkButton role="button">
31
- * // Click me
32
- * // </GtkButton>
33
- * // </GtkApplicationWindow>
34
- * ```
30
+ * @param container A single scope or an array of scopes to format and print.
31
+ * @param options Formatting options passed through to {@link prettyWidget}.
35
32
  */
36
- export declare const prettyWidget: (container: Container, options?: PrettyWidgetOptions) => string;
33
+ declare const logWidget: (container: Container | Container[], options?: PrettyWidgetOptions) => void;
34
+ export { prettyWidget, logWidget, type PrettyWidgetOptions };
37
35
  //# sourceMappingURL=pretty-widget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pretty-widget.d.ts","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,gBAAgB,CAAC;AAkB/D;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAqIF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,SAAS,EAAE,UAAS,mBAAwB,KAAG,MAkBtF,CAAC"}
1
+ {"version":3,"file":"pretty-widget.d.ts","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,gBAAgB,CAAC;AAGvD,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC;AAEvD;;;GAGG;AACH,KAAK,mBAAmB,GAAG;IACvB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA4KF;;;;;;;GAOG;AACH,QAAA,MAAM,YAAY,GAAI,WAAW,SAAS,EAAE,UAAS,mBAAwB,KAAG,MAoB/E,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,SAAS,GAAI,WAAW,SAAS,GAAG,SAAS,EAAE,EAAE,UAAU,mBAAmB,KAAG,IAMtF,CAAC;AAEF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,mBAAmB,EAAE,CAAC"}
@@ -1,161 +1,155 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
1
+ import { sortStringsBy } from "@gtkx/utils";
2
2
  import { formatRole } from "./role-helpers.js";
3
- import { isApplication } from "./traversal.js";
4
- import { getWidgetPropertyText } from "./widget-text.js";
3
+ import { roots } from "./traversal.js";
4
+ import { getWidgetNodeText } from "./widget-accessible-properties.js";
5
5
  const DEFAULT_MAX_LENGTH = 7000;
6
6
  const INDENT = " ";
7
- const debugIdMap = new WeakMap();
8
- let nextDebugId = 0;
9
- const getWidgetDebugId = (widget) => {
10
- let id = debugIdMap.get(widget);
11
- if (!id) {
12
- id = String(nextDebugId++);
13
- debugIdMap.set(widget, id);
14
- }
15
- return id;
7
+ const ansi = {
8
+ cyan: "\u{1B}[36m",
9
+ yellow: "\u{1B}[33m",
10
+ green: "\u{1B}[32m",
11
+ reset: "\u{1B}[0m",
12
+ };
13
+ const buildAttrs = (widget, getId) => {
14
+ const attrs = [];
15
+ if (getId) {
16
+ attrs.push(["id", getId(widget)]);
17
+ }
18
+ const name = widget.getName();
19
+ if (name) {
20
+ attrs.push(["name", name]);
21
+ }
22
+ attrs.push(["role", formatRole(widget.getAccessibleRole())]);
23
+ if (!widget.getSensitive()) {
24
+ attrs.push(["accessible-disabled", "true"]);
25
+ }
26
+ if (!widget.getVisible()) {
27
+ attrs.push(["accessible-hidden", "true"]);
28
+ }
29
+ const idAttrs = attrs.filter(([key]) => key === "id");
30
+ const otherAttrs = sortStringsBy(attrs.filter(([key]) => key !== "id"), ([key]) => key);
31
+ return [...idAttrs, ...otherAttrs];
16
32
  };
17
33
  const shouldHighlight = () => {
18
- if (typeof process === "undefined")
34
+ if (typeof process === "undefined") {
19
35
  return false;
20
- if (process.env.COLORS === "false" || process.env.NO_COLOR)
36
+ }
37
+ if (process.env.COLORS === "false" || process.env.NO_COLOR) {
21
38
  return false;
22
- if (process.env.COLORS === "true" || process.env.FORCE_COLOR)
39
+ }
40
+ if (process.env.COLORS === "true" || process.env.FORCE_COLOR) {
23
41
  return true;
24
- return process.stdout?.isTTY ?? false;
25
- };
26
- const ansi = {
27
- cyan: "\x1b[36m",
28
- yellow: "\x1b[33m",
29
- green: "\x1b[32m",
30
- reset: "\x1b[0m",
42
+ }
43
+ return process.stdout.isTTY;
31
44
  };
32
- const createColors = (enabled) => {
33
- if (!enabled) {
45
+ const createColors = (isEnabled) => {
46
+ if (!isEnabled) {
34
47
  const identity = (s) => s;
35
- return { tag: identity, attr: identity, value: identity, text: identity, reset: "" };
48
+ return { tag: identity, attr: identity, value: identity };
36
49
  }
37
50
  return {
38
51
  tag: (s) => `${ansi.cyan}${s}${ansi.reset}`,
39
52
  attr: (s) => `${ansi.yellow}${s}${ansi.reset}`,
40
53
  value: (s) => `${ansi.green}${s}${ansi.reset}`,
41
- text: (s) => s,
42
- reset: ansi.reset,
43
54
  };
44
55
  };
45
- const formatTagName = (widget) => {
46
- return widget.constructor.name;
47
- };
48
- const escapeAttrValue = (value) => {
49
- return value.replace(/"/g, "&quot;");
56
+ const escapeAttrValue = (value) => value.replaceAll('"', "&quot;");
57
+ const formatAttr = (key, value, colors) => {
58
+ const quoted = `"${escapeAttrValue(value)}"`;
59
+ return ` ${colors.attr(key)}=${colors.value(quoted)}`;
50
60
  };
51
- const formatAttributes = (widget, colors, includeIds) => {
52
- const attrs = [];
53
- if (includeIds) {
54
- attrs.push(["id", getWidgetDebugId(widget)]);
55
- }
56
- const name = widget.getName();
57
- if (name) {
58
- attrs.push(["name", name]);
59
- }
60
- const role = widget.getAccessibleRole();
61
- if (role !== undefined) {
62
- attrs.push(["role", formatRole(role)]);
63
- }
64
- if (!widget.getSensitive()) {
65
- attrs.push(["aria-disabled", "true"]);
66
- }
67
- if (!widget.getVisible()) {
68
- attrs.push(["aria-hidden", "true"]);
61
+ const formatAttrs = (attrs, colors) => attrs.map(([key, value]) => formatAttr(key, value, colors)).join("");
62
+ const countChildren = (widget) => {
63
+ let count = 0;
64
+ let child = widget.getFirstChild();
65
+ while (child) {
66
+ count += 1;
67
+ child = child.getNextSibling();
69
68
  }
70
- if (attrs.length === 0)
71
- return "";
72
- return attrs
73
- .sort(([a], [b]) => {
74
- if (a === "id")
75
- return -1;
76
- if (b === "id")
77
- return 1;
78
- return a.localeCompare(b);
79
- })
80
- .map(([key, value]) => ` ${colors.attr(key)}=${colors.value(`"${escapeAttrValue(value)}"`)}`)
81
- .join("");
82
- };
83
- const hasChildren = (widget) => {
84
- return widget.getFirstChild() !== null;
69
+ return count;
85
70
  };
86
- const printWidget = (widget, colors, depth, includeIds) => {
71
+ const formatHiddenChildrenLine = (widget, depth, ctx) => {
72
+ const { getId, colors } = ctx;
87
73
  const indent = INDENT.repeat(depth);
88
- const tagName = formatTagName(widget);
89
- const attributes = formatAttributes(widget, colors, includeIds);
90
- const text = getWidgetPropertyText(widget);
91
- const children = [];
74
+ const count = countChildren(widget);
75
+ const hint = getId ? ` (pass rootId="${getId(widget)}" or raise maxDepth to expand)` : "";
76
+ const plural = count === 1 ? "" : "s";
77
+ const summary = `… ${String(count)} child widget${plural} hidden${hint}`;
78
+ return `${indent}${INDENT}${colors.tag(summary)}\n`;
79
+ };
80
+ const formatChildren = (widget, depth, ctx) => {
81
+ let output = "";
92
82
  let child = widget.getFirstChild();
93
83
  while (child) {
94
- children.push(printWidget(child, colors, depth + 1, includeIds));
84
+ output += formatWidget(child, depth + 1, ctx);
95
85
  child = child.getNextSibling();
96
86
  }
97
- const openTag = `${colors.tag("<")}${colors.tag(tagName)}${attributes}${colors.tag(">")}`;
98
- if (!hasChildren(widget) && !text) {
87
+ return output;
88
+ };
89
+ const formatWidget = (widget, depth, ctx) => {
90
+ const { getId, colors, maxDepth } = ctx;
91
+ const indent = INDENT.repeat(depth);
92
+ const tag = widget.constructor.name;
93
+ const attrs = formatAttrs(buildAttrs(widget, getId), colors);
94
+ const openTag = `${colors.tag("<")}${colors.tag(tag)}${attrs}${colors.tag(">")}`;
95
+ const closeTag = `${colors.tag("</")}${colors.tag(tag)}${colors.tag(">")}`;
96
+ const text = getWidgetNodeText(widget);
97
+ const firstChild = widget.getFirstChild();
98
+ if (!text && !firstChild) {
99
99
  return `${indent}${openTag}\n`;
100
100
  }
101
- const closeTag = `${colors.tag("</")}${colors.tag(tagName)}${colors.tag(">")}`;
102
- if (text && !hasChildren(widget)) {
103
- const textContent = colors.text(text);
104
- return `${indent}${openTag}\n${indent}${INDENT}${textContent}\n${indent}${closeTag}\n`;
105
- }
106
- let result = `${indent}${openTag}\n`;
101
+ let output = `${indent}${openTag}\n`;
107
102
  if (text) {
108
- result += `${indent}${INDENT}${colors.text(text)}\n`;
103
+ output += `${indent}${INDENT}${text}\n`;
109
104
  }
110
- for (const childOutput of children) {
111
- result += childOutput;
105
+ if (firstChild && maxDepth !== undefined && depth >= maxDepth) {
106
+ output += formatHiddenChildrenLine(widget, depth, ctx);
107
+ output += `${indent}${closeTag}\n`;
108
+ return output;
112
109
  }
113
- result += `${indent}${closeTag}\n`;
114
- return result;
110
+ output += formatChildren(widget, depth, ctx);
111
+ output += `${indent}${closeTag}\n`;
112
+ return output;
115
113
  };
116
- const printContainer = (container, colors, includeIds) => {
117
- if (isApplication(container)) {
118
- const windows = Gtk.Window.listToplevels();
119
- return windows.map((window) => printWidget(window, colors, 0, includeIds)).join("");
120
- }
121
- return printWidget(container, colors, 0, includeIds);
114
+ const resolveMaxLength = (options) => {
115
+ const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;
116
+ return options.maxLength ?? envLimit;
122
117
  };
123
118
  /**
124
- * Formats a widget tree as a readable string for debugging.
125
- *
126
- * Renders the widget hierarchy in an HTML-like format with accessibility
127
- * attributes like role, name, and text content.
119
+ * Renders a widget tree as an indented, HTML-like string showing each widget's
120
+ * tag, accessible attributes, and text content.
128
121
  *
129
- * @param container - The container widget or application to format
130
- * @param options - Formatting options for length and highlighting
131
- * @returns Formatted string representation of the widget tree
132
- *
133
- * @example
134
- * ```tsx
135
- * import { prettyWidget } from "@gtkx/testing";
136
- *
137
- * console.log(prettyWidget(application));
138
- * // Output:
139
- * // <GtkApplicationWindow role="window">
140
- * // <GtkButton role="button">
141
- * // Click me
142
- * // </GtkButton>
143
- * // </GtkApplicationWindow>
144
- * ```
122
+ * @param container The scope whose widget tree is formatted.
123
+ * @param options Formatting options such as truncation length and highlighting.
124
+ * @returns The formatted representation of the tree.
145
125
  */
146
- export const prettyWidget = (container, options = {}) => {
147
- const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;
148
- const maxLength = options.maxLength ?? envLimit;
149
- const highlight = options.highlight ?? shouldHighlight();
150
- const includeIds = options.includeIds ?? false;
126
+ const prettyWidget = (container, options = {}) => {
127
+ const maxLength = resolveMaxLength(options);
151
128
  if (maxLength === 0) {
152
129
  return "";
153
130
  }
154
- const colors = createColors(highlight);
155
- const output = printContainer(container, colors, includeIds);
131
+ const isHighlight = options.highlight ?? shouldHighlight();
132
+ const colors = createColors(isHighlight);
133
+ let output = "";
134
+ for (const root of roots(container)) {
135
+ output += formatWidget(root, 0, { getId: options.getId, colors, maxDepth: options.maxDepth });
136
+ }
156
137
  if (output.length > maxLength) {
157
138
  return `${output.slice(0, maxLength)}...`;
158
139
  }
159
140
  return output.trimEnd();
160
141
  };
142
+ /**
143
+ * Prints one or more widget trees to the console using {@link prettyWidget}.
144
+ *
145
+ * @param container A single scope or an array of scopes to format and print.
146
+ * @param options Formatting options passed through to {@link prettyWidget}.
147
+ */
148
+ const logWidget = (container, options) => {
149
+ const containers = Array.isArray(container) ? container : [container];
150
+ for (const target of containers) {
151
+ console.log(prettyWidget(target, options));
152
+ }
153
+ };
154
+ export { prettyWidget, logWidget };
161
155
  //# sourceMappingURL=pretty-widget.js.map