@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
package/dist/queries.js CHANGED
@@ -1,32 +1,177 @@
1
- import * as Gtk from "@gtkx/ffi/gtk";
2
- import { buildMultipleFoundError, buildNotFoundError } from "./error-builder.js";
1
+ import * as Gtk from "@gtkx/gi/gtk";
2
+ import { buildQueries } from "./build-queries.js";
3
+ import { multipleFoundError, notFoundError } from "./errors.js";
3
4
  import { findAll, traverse } from "./traversal.js";
4
- import { waitFor } from "./wait-for.js";
5
- import { getWidgetAccessibleName, getWidgetCheckedState, getWidgetExpandedState, getWidgetName, getWidgetPressedState, getWidgetSelectedState, getWidgetText, } from "./widget-text.js";
6
- const buildNormalizer = (options) => {
7
- if (options?.normalizer) {
8
- return options.normalizer;
9
- }
10
- const trim = options?.trim ?? true;
11
- const collapseWhitespace = options?.collapseWhitespace ?? true;
5
+ import { getWidgetAccessibleName, getWidgetBusyState, getWidgetCheckedState, getWidgetDescription, getWidgetDisplayValue, getWidgetExpandedState, getWidgetLabelledByText, getWidgetLabelText, getWidgetLevel, getWidgetName, getWidgetOwnLabel, getWidgetPlaceholderText, getWidgetPressedState, getWidgetSelectedState, getWidgetValue, isInaccessible, } from "./widget-accessible-properties.js";
6
+ const roleQueries = nameQueryFamily("Role", queryAllByRole, buildQueries("Role", queryAllByRole, (container, matches, role, options) => multipleFoundError(container, { queryType: "role", role, options }, matches), (container, role, options) => notFoundError(container, { queryType: "role", role, options })));
7
+ const labelTextQueries = nameQueryFamily("LabelText", queryAllByLabelText, buildQueries("LabelText", queryAllByLabelText, (container, matches, text) => multipleFoundError(container, { queryType: "labelText", text }, matches), (container, text) => notFoundError(container, { queryType: "labelText", text })));
8
+ const textQueries = nameQueryFamily("Text", queryAllByText, buildQueries("Text", queryAllByText, (container, matches, text) => multipleFoundError(container, { queryType: "text", text }, matches), (container, text) => notFoundError(container, { queryType: "text", text })));
9
+ const nameQueries = nameQueryFamily("Name", queryAllByName, buildQueries("Name", queryAllByName, (container, matches, name) => multipleFoundError(container, { queryType: "name", name }, matches), (container, name) => notFoundError(container, { queryType: "name", name })));
10
+ const placeholderTextQueries = nameQueryFamily("PlaceholderText", queryAllByPlaceholderText, buildQueries("PlaceholderText", queryAllByPlaceholderText, (container, matches, text) => multipleFoundError(container, { queryType: "placeholderText", text }, matches), (container, text) => notFoundError(container, { queryType: "placeholderText", text })));
11
+ const displayValueQueries = nameQueryFamily("DisplayValue", queryAllByDisplayValue, buildQueries("DisplayValue", queryAllByDisplayValue, (container, matches, value) => multipleFoundError(container, { queryType: "displayValue", value }, matches), (container, value) => notFoundError(container, { queryType: "displayValue", value })));
12
+ const builtinQueries = {
13
+ ...roleQueries,
14
+ ...labelTextQueries,
15
+ ...textQueries,
16
+ ...nameQueries,
17
+ ...placeholderTextQueries,
18
+ ...displayValueQueries,
19
+ };
20
+ /**
21
+ * Returns the single widget with a matching accessible role and options, or null when none match.
22
+ * Throws when more than one matches.
23
+ */
24
+ const queryByRole = roleQueries.queryByRole;
25
+ /** Returns every widget with a matching accessible role and options. Throws when none match. */
26
+ const getAllByRole = roleQueries.getAllByRole;
27
+ /**
28
+ * Returns the single widget with a matching accessible role and options.
29
+ * Throws when none or more than one matches.
30
+ */
31
+ const getByRole = roleQueries.getByRole;
32
+ /**
33
+ * Waits for and returns every widget with a matching accessible role and options, retrying until at
34
+ * least one appears or the timeout elapses.
35
+ */
36
+ const findAllByRole = roleQueries.findAllByRole;
37
+ /**
38
+ * Waits for and returns the single widget with a matching accessible role and options, retrying
39
+ * until it appears or the timeout elapses. Rejects when none or more than one matches.
40
+ */
41
+ const findByRole = roleQueries.findByRole;
42
+ /**
43
+ * Returns the single widget with matching associated label text, or null when none match.
44
+ * Throws when more than one matches.
45
+ */
46
+ const queryByLabelText = labelTextQueries.queryByLabelText;
47
+ /** Returns every widget with matching associated label text. Throws when none match. */
48
+ const getAllByLabelText = labelTextQueries.getAllByLabelText;
49
+ /** Returns the single widget with matching associated label text. Throws when none or more than one matches. */
50
+ const getByLabelText = labelTextQueries.getByLabelText;
51
+ /**
52
+ * Waits for and returns every widget with matching associated label text, retrying until at least
53
+ * one appears or the timeout elapses.
54
+ */
55
+ const findAllByLabelText = labelTextQueries.findAllByLabelText;
56
+ /**
57
+ * Waits for and returns the single widget with matching associated label text, retrying until it
58
+ * appears or the timeout elapses. Rejects when none or more than one matches.
59
+ */
60
+ const findByLabelText = labelTextQueries.findByLabelText;
61
+ /**
62
+ * Returns the single widget with matching rendered text content, or null when none match.
63
+ * Throws when more than one matches.
64
+ */
65
+ const queryByText = textQueries.queryByText;
66
+ /** Returns every widget with matching rendered text content. Throws when none match. */
67
+ const getAllByText = textQueries.getAllByText;
68
+ /** Returns the single widget with matching rendered text content. Throws when none or more than one matches. */
69
+ const getByText = textQueries.getByText;
70
+ /**
71
+ * Waits for and returns every widget with matching rendered text content, retrying until at least
72
+ * one appears or the timeout elapses.
73
+ */
74
+ const findAllByText = textQueries.findAllByText;
75
+ /**
76
+ * Waits for and returns the single widget with matching rendered text content, retrying until it
77
+ * appears or the timeout elapses. Rejects when none or more than one matches.
78
+ */
79
+ const findByText = textQueries.findByText;
80
+ /**
81
+ * Returns the single widget with a matching widget name, or null when none match.
82
+ * Throws when more than one matches.
83
+ */
84
+ const queryByName = nameQueries.queryByName;
85
+ /** Returns every widget with a matching widget name. Throws when none match. */
86
+ const getAllByName = nameQueries.getAllByName;
87
+ /** Returns the single widget with a matching widget name. Throws when none or more than one matches. */
88
+ const getByName = nameQueries.getByName;
89
+ /**
90
+ * Waits for and returns every widget with a matching widget name, retrying until at least one
91
+ * appears or the timeout elapses.
92
+ */
93
+ const findAllByName = nameQueries.findAllByName;
94
+ /**
95
+ * Waits for and returns the single widget with a matching widget name, retrying until it appears or
96
+ * the timeout elapses. Rejects when none or more than one matches.
97
+ */
98
+ const findByName = nameQueries.findByName;
99
+ /**
100
+ * Returns the single widget with matching placeholder text, or null when none match.
101
+ * Throws when more than one matches.
102
+ */
103
+ const queryByPlaceholderText = placeholderTextQueries.queryByPlaceholderText;
104
+ /** Returns every widget with matching placeholder text. Throws when none match. */
105
+ const getAllByPlaceholderText = placeholderTextQueries.getAllByPlaceholderText;
106
+ /** Returns the single widget with matching placeholder text. Throws when none or more than one matches. */
107
+ const getByPlaceholderText = placeholderTextQueries.getByPlaceholderText;
108
+ /**
109
+ * Waits for and returns every widget with matching placeholder text, retrying until at least one
110
+ * appears or the timeout elapses.
111
+ */
112
+ const findAllByPlaceholderText = placeholderTextQueries.findAllByPlaceholderText;
113
+ /**
114
+ * Waits for and returns the single widget with matching placeholder text, retrying until it appears
115
+ * or the timeout elapses. Rejects when none or more than one matches.
116
+ */
117
+ const findByPlaceholderText = placeholderTextQueries.findByPlaceholderText;
118
+ /**
119
+ * Returns the single widget with a matching display value, or null when none match.
120
+ * Throws when more than one matches.
121
+ */
122
+ const queryByDisplayValue = displayValueQueries.queryByDisplayValue;
123
+ /** Returns every widget with a matching display value. Throws when none match. */
124
+ const getAllByDisplayValue = displayValueQueries.getAllByDisplayValue;
125
+ /** Returns the single widget with a matching display value. Throws when none or more than one matches. */
126
+ const getByDisplayValue = displayValueQueries.getByDisplayValue;
127
+ /**
128
+ * Waits for and returns every widget with a matching display value, retrying until at least one
129
+ * appears or the timeout elapses.
130
+ */
131
+ const findAllByDisplayValue = displayValueQueries.findAllByDisplayValue;
132
+ /**
133
+ * Waits for and returns the single widget with a matching display value, retrying until it appears
134
+ * or the timeout elapses. Rejects when none or more than one matches.
135
+ */
136
+ const findByDisplayValue = displayValueQueries.findByDisplayValue;
137
+ /**
138
+ * Builds the default text normalizer, which optionally trims surrounding whitespace and collapses
139
+ * runs of whitespace into single spaces.
140
+ * @param options Toggles for trimming and whitespace collapsing.
141
+ * @returns A function that normalizes a string for comparison against a matcher.
142
+ */
143
+ const getDefaultNormalizer = ({ trim = true, collapseWhitespace = true, } = {}) => {
12
144
  return (text) => {
13
145
  let result = text;
14
146
  if (trim) {
15
147
  result = result.trim();
16
148
  }
17
149
  if (collapseWhitespace) {
18
- result = result.replace(/\s+/g, " ");
150
+ result = result.replaceAll(/\s+/g, " ");
19
151
  }
20
152
  return result;
21
153
  };
22
154
  };
155
+ const buildNormalizer = (options) => {
156
+ const { normalizer, trim, collapseWhitespace } = options ?? {};
157
+ if (!normalizer) {
158
+ return getDefaultNormalizer({ trim, collapseWhitespace });
159
+ }
160
+ if (trim !== undefined || collapseWhitespace !== undefined) {
161
+ throw new Error("trim and collapseWhitespace are not supported with a normalizer. " +
162
+ "If you want to use the default trim and collapseWhitespace logic in your normalizer, " +
163
+ "use \"getDefaultNormalizer({ trim, collapseWhitespace })\" and compose that into your normalizer");
164
+ }
165
+ return normalizer;
166
+ };
23
167
  const normalizeText = (text, options) => {
24
168
  const normalizer = buildNormalizer(options);
25
169
  return normalizer(text);
26
170
  };
27
- const matchText = (actual, expected, widget, options) => {
28
- if (actual === null)
171
+ const isTextMatch = (actual, expected, widget, options) => {
172
+ if (actual === null) {
29
173
  return false;
174
+ }
30
175
  const normalizedActual = normalizeText(actual, options);
31
176
  if (typeof expected === "function") {
32
177
  return expected(normalizedActual, widget);
@@ -35,345 +180,171 @@ const matchText = (actual, expected, widget, options) => {
35
180
  expected.lastIndex = 0;
36
181
  return expected.test(normalizedActual);
37
182
  }
38
- const normalizedExpected = normalizeText(expected, options);
39
- const exact = options?.exact ?? true;
40
- return exact
183
+ const normalizedExpected = normalizeText(String(expected), options);
184
+ const isExact = options?.exact ?? true;
185
+ return isExact
41
186
  ? normalizedActual === normalizedExpected
42
187
  : normalizedActual.toLowerCase().includes(normalizedExpected.toLowerCase());
43
188
  };
44
- const matchByRoleOptions = (widget, options) => {
45
- if (!options)
189
+ const hasMatchingAccessibleName = (widget, options) => {
190
+ if (options.name === undefined) {
46
191
  return true;
47
- if (options.name !== undefined) {
48
- const text = getWidgetAccessibleName(widget);
49
- if (!matchText(text, options.name, widget, options))
50
- return false;
51
- }
52
- if (options.checked !== undefined) {
53
- const checked = getWidgetCheckedState(widget);
54
- if (checked !== options.checked)
55
- return false;
56
- }
57
- if (options.pressed !== undefined) {
58
- const pressed = getWidgetPressedState(widget);
59
- if (pressed !== options.pressed)
60
- return false;
61
192
  }
62
- if (options.expanded !== undefined) {
63
- const expanded = getWidgetExpandedState(widget);
64
- if (expanded !== options.expanded)
65
- return false;
66
- }
67
- if (options.selected !== undefined) {
68
- const selected = getWidgetSelectedState(widget);
69
- if (selected !== options.selected)
70
- return false;
71
- }
72
- return true;
193
+ const text = getWidgetAccessibleName(widget);
194
+ return isTextMatch(text, options.name, widget, options);
73
195
  };
74
- /**
75
- * Finds all elements matching a role without throwing.
76
- *
77
- * @param container - The container to search within
78
- * @param role - The GTK accessible role to match
79
- * @param options - Query options including name and state filters
80
- * @returns Array of matching widgets (empty if none found)
81
- */
82
- export const queryAllByRole = (container, role, options) => {
83
- return findAll(container, (node) => {
84
- if (node.getAccessibleRole() !== role)
196
+ const isNumericValueMatch = (expected, actual) => expected === undefined || actual === expected;
197
+ const hasMatchingAccessibleValue = (widget, value, options) => {
198
+ const actual = getWidgetValue(widget);
199
+ const numericChecks = [
200
+ [value.now, actual.now],
201
+ [value.min, actual.min],
202
+ [value.max, actual.max],
203
+ ];
204
+ for (const [expected, current] of numericChecks) {
205
+ if (!isNumericValueMatch(expected, current)) {
85
206
  return false;
86
- return matchByRoleOptions(node, options);
87
- });
88
- };
89
- /**
90
- * Finds a single element matching a role without throwing.
91
- *
92
- * @param container - The container to search within
93
- * @param role - The GTK accessible role to match
94
- * @param options - Query options including name and state filters
95
- * @returns The matching widget or null if not found
96
- * @throws Error if multiple elements match
97
- */
98
- export const queryByRole = (container, role, options) => {
99
- const matches = queryAllByRole(container, role, options);
100
- if (matches.length > 1) {
101
- throw buildMultipleFoundError(container, "role", { role, options }, matches.length);
102
- }
103
- return matches[0] ?? null;
104
- };
105
- /**
106
- * Finds all elements that are labelled by a GtkLabel whose text matches.
107
- *
108
- * Uses GtkLabel's mnemonic widget association to find form elements
109
- * by their label text. Only returns widgets that are properly labelled
110
- * via GtkLabel's mnemonic-widget property.
111
- *
112
- * @param container - The container to search within
113
- * @param text - Label text to match (string, RegExp, or custom matcher)
114
- * @param options - Query options including normalization
115
- * @returns Array of labelled widgets (empty if none found)
116
- */
117
- export const queryAllByLabelText = (container, text, options) => {
118
- const results = [];
119
- for (const node of traverse(container)) {
120
- if (!(node instanceof Gtk.Label))
121
- continue;
122
- const labelText = node.getLabel();
123
- if (!labelText)
124
- continue;
125
- if (!matchText(labelText, text, node, options))
126
- continue;
127
- const target = node.getMnemonicWidget();
128
- if (target) {
129
- results.push(target);
130
207
  }
131
208
  }
132
- return results;
209
+ return value.text === undefined || isTextMatch(actual.text, value.text, widget, options);
133
210
  };
134
- /**
135
- * Finds a single element matching label text without throwing.
136
- *
137
- * @param container - The container to search within
138
- * @param text - Text to match (string, RegExp, or custom matcher)
139
- * @param options - Query options including normalization
140
- * @returns The matching widget or null if not found
141
- * @throws Error if multiple elements match
142
- */
143
- export const queryByLabelText = (container, text, options) => {
144
- const matches = queryAllByLabelText(container, text, options);
145
- if (matches.length > 1) {
146
- throw buildMultipleFoundError(container, "labelText", { text, options }, matches.length);
211
+ const hasMatchingBooleanStates = (widget, options) => {
212
+ const stateChecks = [
213
+ [options.checked, () => getWidgetCheckedState(widget)],
214
+ [options.pressed, () => getWidgetPressedState(widget)],
215
+ [options.expanded, () => getWidgetExpandedState(widget)],
216
+ [options.selected, () => getWidgetSelectedState(widget)],
217
+ [options.busy, () => getWidgetBusyState(widget) ?? false],
218
+ ];
219
+ for (const [expected, getActual] of stateChecks) {
220
+ if (expected !== undefined && getActual() !== expected) {
221
+ return false;
222
+ }
147
223
  }
148
- return matches[0] ?? null;
149
- };
150
- /**
151
- * Finds all elements matching text content without throwing.
152
- *
153
- * @param container - The container to search within
154
- * @param text - Text to match (string, RegExp, or custom matcher)
155
- * @param options - Query options including normalization
156
- * @returns Array of matching widgets (empty if none found)
157
- */
158
- export const queryAllByText = (container, text, options) => {
159
- return findAll(container, (node) => {
160
- return matchText(getWidgetText(node), text, node, options);
161
- });
224
+ return true;
162
225
  };
163
- /**
164
- * Finds a single element matching text content without throwing.
165
- *
166
- * @param container - The container to search within
167
- * @param text - Text to match (string, RegExp, or custom matcher)
168
- * @param options - Query options including normalization
169
- * @returns The matching widget or null if not found
170
- * @throws Error if multiple elements match
171
- */
172
- export const queryByText = (container, text, options) => {
173
- const matches = queryAllByText(container, text, options);
174
- if (matches.length > 1) {
175
- throw buildMultipleFoundError(container, "text", { text, options }, matches.length);
226
+ const hasMatchingLevelState = (widget, options) => options.level === undefined || getWidgetLevel(widget) === options.level;
227
+ const hasMatchingDescriptionState = (widget, options) => options.description === undefined ||
228
+ isTextMatch(getWidgetDescription(widget), options.description, widget, options);
229
+ const hasMatchingValueState = (widget, options) => options.value === undefined || hasMatchingAccessibleValue(widget, options.value, options);
230
+ const hasMatchingAccessibleStates = (widget, options) => hasMatchingBooleanStates(widget, options) &&
231
+ hasMatchingLevelState(widget, options) &&
232
+ hasMatchingDescriptionState(widget, options) &&
233
+ hasMatchingValueState(widget, options);
234
+ const hasMatchingByRoleOptions = (widget, options) => {
235
+ if (!options) {
236
+ return true;
176
237
  }
177
- return matches[0] ?? null;
238
+ return hasMatchingAccessibleName(widget, options) && hasMatchingAccessibleStates(widget, options);
178
239
  };
240
+ function nameQueryFamily(suffix, queryAllBy, built) {
241
+ const family = {
242
+ [`queryBy${suffix}`]: built.queryBy,
243
+ [`queryAllBy${suffix}`]: queryAllBy,
244
+ [`getBy${suffix}`]: built.getBy,
245
+ [`getAllBy${suffix}`]: built.getAllBy,
246
+ [`findBy${suffix}`]: built.findBy,
247
+ [`findAllBy${suffix}`]: built.findAllBy,
248
+ };
249
+ return family;
250
+ }
179
251
  /**
180
- * Finds all elements matching a widget name without throwing.
181
- *
182
- * @param container - The container to search within
183
- * @param name - Widget name to match (string, RegExp, or custom matcher)
184
- * @param options - Query options including normalization
185
- * @returns Array of matching widgets (empty if none found)
252
+ * Finds every widget under the container whose accessible role matches `role` and that satisfies the given options.
253
+ * Widgets excluded from the accessibility tree are skipped unless `options.hidden` is set.
254
+ * @param container Widget subtree to search.
255
+ * @param role Accessible role to match.
256
+ * @param options Additional accessible name, state, and value constraints.
257
+ * @returns Every matching widget, or an empty array when none match.
186
258
  */
187
- export const queryAllByName = (container, name, options) => {
188
- return findAll(container, (node) => {
189
- const widgetName = getWidgetName(node);
190
- return matchText(widgetName, name, node, options);
259
+ function queryAllByRole(container, role, options) {
260
+ return findAll(container, (widget) => {
261
+ if (widget.getAccessibleRole() !== role) {
262
+ return false;
263
+ }
264
+ if (!options?.hidden && isInaccessible(widget)) {
265
+ return false;
266
+ }
267
+ return hasMatchingByRoleOptions(widget, options);
191
268
  });
192
- };
193
- /**
194
- * Finds a single element matching a widget name without throwing.
195
- *
196
- * @param container - The container to search within
197
- * @param name - Widget name to match (string, RegExp, or custom matcher)
198
- * @param options - Query options including normalization
199
- * @returns The matching widget or null if not found
200
- * @throws Error if multiple elements match
201
- */
202
- export const queryByName = (container, name, options) => {
203
- const matches = queryAllByName(container, name, options);
204
- if (matches.length > 1) {
205
- throw buildMultipleFoundError(container, "name", { name, options }, matches.length);
206
- }
207
- return matches[0] ?? null;
208
- };
209
- const getAllByRole = (container, role, options) => {
210
- const matches = queryAllByRole(container, role, options);
211
- if (matches.length === 0) {
212
- throw buildNotFoundError(container, "role", { role, options });
213
- }
214
- return matches;
215
- };
216
- const getByRole = (container, role, options) => {
217
- const matches = getAllByRole(container, role, options);
218
- if (matches.length > 1) {
219
- throw buildMultipleFoundError(container, "role", { role, options }, matches.length);
220
- }
221
- return matches[0];
222
- };
223
- const getAllByLabelText = (container, text, options) => {
224
- const matches = queryAllByLabelText(container, text, options);
225
- if (matches.length === 0) {
226
- throw buildNotFoundError(container, "labelText", { text, options });
269
+ }
270
+ const collectMnemonicMatch = (widget, text, options) => {
271
+ if (!(widget instanceof Gtk.Label)) {
272
+ return null;
227
273
  }
228
- return matches;
229
- };
230
- const getByLabelText = (container, text, options) => {
231
- const matches = getAllByLabelText(container, text, options);
232
- if (matches.length > 1) {
233
- throw buildMultipleFoundError(container, "labelText", { text, options }, matches.length);
274
+ const labelText = widget.getLabel();
275
+ if (!labelText || !isTextMatch(labelText, text, widget, options)) {
276
+ return null;
234
277
  }
235
- return matches[0];
278
+ return widget.getMnemonicWidget();
236
279
  };
237
- const getAllByText = (container, text, options) => {
238
- const matches = queryAllByText(container, text, options);
239
- if (matches.length === 0) {
240
- throw buildNotFoundError(container, "text", { text, options });
280
+ const collectLabelMatches = (results, widget, text, options) => {
281
+ const mnemonicTarget = collectMnemonicMatch(widget, text, options);
282
+ if (mnemonicTarget) {
283
+ results.add(mnemonicTarget);
241
284
  }
242
- return matches;
243
- };
244
- const getByText = (container, text, options) => {
245
- const matches = getAllByText(container, text, options);
246
- if (matches.length > 1) {
247
- throw buildMultipleFoundError(container, "text", { text, options }, matches.length);
285
+ const ownLabel = getWidgetOwnLabel(widget);
286
+ if (ownLabel !== null && isTextMatch(ownLabel, text, widget, options)) {
287
+ results.add(widget);
248
288
  }
249
- return matches[0];
250
- };
251
- const getAllByName = (container, name, options) => {
252
- const matches = queryAllByName(container, name, options);
253
- if (matches.length === 0) {
254
- throw buildNotFoundError(container, "name", { name, options });
289
+ const labelledByText = getWidgetLabelledByText(widget);
290
+ if (labelledByText !== null && isTextMatch(labelledByText, text, widget, options)) {
291
+ results.add(widget);
255
292
  }
256
- return matches;
257
- };
258
- const getByName = (container, name, options) => {
259
- const matches = getAllByName(container, name, options);
260
- if (matches.length > 1) {
261
- throw buildMultipleFoundError(container, "name", { name, options }, matches.length);
262
- }
263
- return matches[0];
264
293
  };
265
294
  /**
266
- * Finds a single element by accessible role.
267
- *
268
- * Waits for the element to appear, throwing if not found within timeout.
269
- *
270
- * @param container - The container to search within
271
- * @param role - The GTK accessible role to match
272
- * @param options - Query options including name, state filters, and timeout
273
- * @returns Promise resolving to the matching widget
274
- *
275
- * @example
276
- * ```tsx
277
- * const button = await findByRole(container, Gtk.AccessibleRole.BUTTON, { name: "Submit" });
278
- * ```
295
+ * Finds every widget associated with a label whose text matches: a Gtk.Label mnemonic target, the
296
+ * widget's own accessible label, or its labelled-by relation.
297
+ * @param container Widget subtree to search.
298
+ * @param text Matcher for the label text.
299
+ * @param options Text matching options.
300
+ * @returns Every matching widget, or an empty array when none match.
279
301
  */
280
- export const findByRole = async (container, role, options) => waitFor(() => getByRole(container, role, options), {
281
- timeout: options?.timeout,
282
- });
283
- /**
284
- * Finds all elements matching an accessible role.
285
- *
286
- * @param container - The container to search within
287
- * @param role - The GTK accessible role to match
288
- * @param options - Query options including name, state filters, and timeout
289
- * @returns Promise resolving to array of matching widgets
290
- */
291
- export const findAllByRole = async (container, role, options) => waitFor(() => getAllByRole(container, role, options), {
292
- timeout: options?.timeout,
293
- });
294
- /**
295
- * Finds a single element that is labelled by a GtkLabel whose text matches.
296
- *
297
- * Waits for the element to appear, throwing if not found within timeout.
298
- * Uses GtkLabel's mnemonic widget association to find form elements.
299
- *
300
- * @param container - The container to search within
301
- * @param text - Label text to match (string, RegExp, or custom matcher)
302
- * @param options - Query options including normalization and timeout
303
- * @returns Promise resolving to the labelled widget
304
- *
305
- * @example
306
- * ```tsx
307
- * const input = await findByLabelText(container, "Username");
308
- * ```
309
- */
310
- export const findByLabelText = async (container, text, options) => waitFor(() => getByLabelText(container, text, options), {
311
- timeout: options?.timeout,
312
- });
313
- /**
314
- * Finds all elements matching label or text content.
315
- *
316
- * @param container - The container to search within
317
- * @param text - Text to match (string, RegExp, or custom matcher)
318
- * @param options - Query options including normalization and timeout
319
- * @returns Promise resolving to array of matching widgets
320
- */
321
- export const findAllByLabelText = async (container, text, options) => waitFor(() => getAllByLabelText(container, text, options), {
322
- timeout: options?.timeout,
323
- });
302
+ function queryAllByLabelText(container, text, options) {
303
+ const results = new Set();
304
+ for (const widget of traverse(container)) {
305
+ collectLabelMatches(results, widget, text, options);
306
+ }
307
+ return [...results];
308
+ }
324
309
  /**
325
- * Finds a single element by visible text content.
326
- *
327
- * Similar to {@link findByLabelText} but focuses on directly visible text.
328
- *
329
- * @param container - The container to search within
330
- * @param text - Text to match (string, RegExp, or custom matcher)
331
- * @param options - Query options including normalization and timeout
332
- * @returns Promise resolving to the matching widget
310
+ * Finds every widget whose rendered text content matches.
311
+ * @param container Widget subtree to search.
312
+ * @param text Matcher for the widget text.
313
+ * @param options Text matching options.
314
+ * @returns Every matching widget, or an empty array when none match.
333
315
  */
334
- export const findByText = async (container, text, options) => waitFor(() => getByText(container, text, options), {
335
- timeout: options?.timeout,
336
- });
316
+ function queryAllByText(container, text, options) {
317
+ return findAll(container, (widget) => isTextMatch(getWidgetLabelText(widget), text, widget, options));
318
+ }
337
319
  /**
338
- * Finds all elements matching visible text content.
339
- *
340
- * @param container - The container to search within
341
- * @param text - Text to match (string, RegExp, or custom matcher)
342
- * @param options - Query options including normalization and timeout
343
- * @returns Promise resolving to array of matching widgets
320
+ * Finds every widget whose widget name matches.
321
+ * @param container Widget subtree to search.
322
+ * @param name Matcher for the widget name.
323
+ * @param options Text matching options.
324
+ * @returns Every matching widget, or an empty array when none match.
344
325
  */
345
- export const findAllByText = async (container, text, options) => waitFor(() => getAllByText(container, text, options), {
346
- timeout: options?.timeout,
347
- });
326
+ function queryAllByName(container, name, options) {
327
+ return findAll(container, (widget) => isTextMatch(getWidgetName(widget), name, widget, options));
328
+ }
348
329
  /**
349
- * Finds a single element by widget name.
350
- *
351
- * Uses the widget's `name` property (gtk_widget_get_name).
352
- * Set via the `name` prop on GTKX components.
353
- *
354
- * @param container - The container to search within
355
- * @param name - Widget name to match (string, RegExp, or custom matcher)
356
- * @param options - Query options including normalization and timeout
357
- * @returns Promise resolving to the matching widget
358
- *
359
- * @example
360
- * ```tsx
361
- * // In component: <GtkButton name="submit-btn" />
362
- * const button = await findByName(container, "submit-btn");
363
- * ```
330
+ * Finds every widget whose placeholder text matches.
331
+ * @param container Widget subtree to search.
332
+ * @param text Matcher for the placeholder text.
333
+ * @param options Text matching options.
334
+ * @returns Every matching widget, or an empty array when none match.
364
335
  */
365
- export const findByName = async (container, name, options) => waitFor(() => getByName(container, name, options), {
366
- timeout: options?.timeout,
367
- });
336
+ function queryAllByPlaceholderText(container, text, options) {
337
+ return findAll(container, (widget) => isTextMatch(getWidgetPlaceholderText(widget), text, widget, options));
338
+ }
368
339
  /**
369
- * Finds all elements matching a widget name pattern.
370
- *
371
- * @param container - The container to search within
372
- * @param name - Widget name to match (string, RegExp, or custom matcher)
373
- * @param options - Query options including normalization and timeout
374
- * @returns Promise resolving to array of matching widgets
340
+ * Finds every widget whose current display value matches.
341
+ * @param container Widget subtree to search.
342
+ * @param value Matcher for the display value.
343
+ * @param options Text matching options.
344
+ * @returns Every matching widget, or an empty array when none match.
375
345
  */
376
- export const findAllByName = async (container, name, options) => waitFor(() => getAllByName(container, name, options), {
377
- timeout: options?.timeout,
378
- });
346
+ function queryAllByDisplayValue(container, value, options) {
347
+ return findAll(container, (widget) => isTextMatch(getWidgetDisplayValue(widget), value, widget, options));
348
+ }
349
+ export { builtinQueries, queryByRole, getAllByRole, getByRole, findAllByRole, findByRole, queryByLabelText, getAllByLabelText, getByLabelText, findAllByLabelText, findByLabelText, queryByText, getAllByText, getByText, findAllByText, findByText, queryByName, getAllByName, getByName, findAllByName, findByName, queryByPlaceholderText, getAllByPlaceholderText, getByPlaceholderText, findAllByPlaceholderText, findByPlaceholderText, queryByDisplayValue, getAllByDisplayValue, getByDisplayValue, findAllByDisplayValue, findByDisplayValue, getDefaultNormalizer, queryAllByRole, queryAllByLabelText, queryAllByText, queryAllByName, queryAllByPlaceholderText, queryAllByDisplayValue, };
379
350
  //# sourceMappingURL=queries.js.map