@gtkx/testing 1.0.0-rc.2 → 1.0.0-rc.4

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 (121) hide show
  1. package/README.md +4 -5
  2. package/dist/act.d.ts +2 -0
  3. package/dist/act.d.ts.map +1 -1
  4. package/dist/act.js.map +1 -1
  5. package/dist/bound-queries.d.ts +2 -3
  6. package/dist/bound-queries.d.ts.map +1 -1
  7. package/dist/bound-queries.js.map +1 -1
  8. package/dist/build-queries.d.ts.map +1 -1
  9. package/dist/build-queries.js +18 -7
  10. package/dist/build-queries.js.map +1 -1
  11. package/dist/config.d.ts +1 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +3 -3
  14. package/dist/config.js.map +1 -1
  15. package/dist/errors.js +2 -2
  16. package/dist/errors.js.map +1 -1
  17. package/dist/index.d.ts +7 -4
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/matchers.d.ts +125 -21
  22. package/dist/matchers.d.ts.map +1 -1
  23. package/dist/matchers.js +258 -21
  24. package/dist/matchers.js.map +1 -1
  25. package/dist/normalize.d.ts +10 -0
  26. package/dist/normalize.d.ts.map +1 -0
  27. package/dist/normalize.js +20 -0
  28. package/dist/normalize.js.map +1 -0
  29. package/dist/pretty-widget.d.ts +2 -1
  30. package/dist/pretty-widget.d.ts.map +1 -1
  31. package/dist/pretty-widget.js +3 -3
  32. package/dist/pretty-widget.js.map +1 -1
  33. package/dist/queries.d.ts +4 -21
  34. package/dist/queries.d.ts.map +1 -1
  35. package/dist/queries.js +46 -59
  36. package/dist/queries.js.map +1 -1
  37. package/dist/render.js +3 -3
  38. package/dist/render.js.map +1 -1
  39. package/dist/suggestions.d.ts +3 -0
  40. package/dist/suggestions.d.ts.map +1 -1
  41. package/dist/suggestions.js.map +1 -1
  42. package/dist/traversal.d.ts +5 -0
  43. package/dist/traversal.d.ts.map +1 -1
  44. package/dist/traversal.js +1 -0
  45. package/dist/traversal.js.map +1 -1
  46. package/dist/types.d.ts +69 -5
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js.map +1 -1
  49. package/dist/user-event/adjustment.d.ts +13 -0
  50. package/dist/user-event/adjustment.d.ts.map +1 -1
  51. package/dist/user-event/adjustment.js +11 -0
  52. package/dist/user-event/adjustment.js.map +1 -1
  53. package/dist/user-event/click.d.ts +7 -0
  54. package/dist/user-event/click.d.ts.map +1 -1
  55. package/dist/user-event/click.js +7 -0
  56. package/dist/user-event/click.js.map +1 -1
  57. package/dist/user-event/controller.d.ts +31 -1
  58. package/dist/user-event/controller.d.ts.map +1 -1
  59. package/dist/user-event/controller.js +36 -1
  60. package/dist/user-event/controller.js.map +1 -1
  61. package/dist/user-event/gesture.d.ts +51 -0
  62. package/dist/user-event/gesture.d.ts.map +1 -1
  63. package/dist/user-event/gesture.js +47 -4
  64. package/dist/user-event/gesture.js.map +1 -1
  65. package/dist/user-event/index.d.ts +27 -0
  66. package/dist/user-event/index.d.ts.map +1 -1
  67. package/dist/user-event/index.js.map +1 -1
  68. package/dist/user-event/keyboard.d.ts +4 -2
  69. package/dist/user-event/keyboard.d.ts.map +1 -1
  70. package/dist/user-event/keyboard.js +13 -12
  71. package/dist/user-event/keyboard.js.map +1 -1
  72. package/dist/user-event/pointer.js +5 -5
  73. package/dist/user-event/pointer.js.map +1 -1
  74. package/dist/user-event/selection.d.ts +15 -0
  75. package/dist/user-event/selection.d.ts.map +1 -1
  76. package/dist/user-event/selection.js +15 -0
  77. package/dist/user-event/selection.js.map +1 -1
  78. package/dist/user-event/state.d.ts +1 -1
  79. package/dist/user-event/state.d.ts.map +1 -1
  80. package/dist/user-event/state.js +1 -1
  81. package/dist/user-event/state.js.map +1 -1
  82. package/dist/user-event/text.d.ts +30 -3
  83. package/dist/user-event/text.d.ts.map +1 -1
  84. package/dist/user-event/text.js +28 -1
  85. package/dist/user-event/text.js.map +1 -1
  86. package/dist/wait-for.d.ts +2 -0
  87. package/dist/wait-for.d.ts.map +1 -1
  88. package/dist/wait-for.js.map +1 -1
  89. package/dist/widget-accessible-properties.d.ts +28 -2
  90. package/dist/widget-accessible-properties.d.ts.map +1 -1
  91. package/dist/widget-accessible-properties.js +129 -10
  92. package/dist/widget-accessible-properties.js.map +1 -1
  93. package/dist/within.js.map +1 -1
  94. package/package.json +7 -7
  95. package/src/act.ts +2 -0
  96. package/src/bound-queries.ts +2 -3
  97. package/src/build-queries.ts +20 -8
  98. package/src/config.ts +4 -3
  99. package/src/errors.ts +2 -2
  100. package/src/index.ts +11 -2
  101. package/src/matchers.ts +500 -53
  102. package/src/normalize.ts +28 -0
  103. package/src/pretty-widget.ts +5 -4
  104. package/src/queries.ts +79 -105
  105. package/src/render.tsx +3 -3
  106. package/src/suggestions.ts +3 -0
  107. package/src/traversal.ts +5 -0
  108. package/src/types.ts +88 -4
  109. package/src/user-event/adjustment.ts +13 -0
  110. package/src/user-event/click.ts +7 -0
  111. package/src/user-event/controller.ts +50 -1
  112. package/src/user-event/gesture.ts +57 -6
  113. package/src/user-event/index.ts +27 -0
  114. package/src/user-event/keyboard.ts +18 -16
  115. package/src/user-event/pointer.ts +5 -5
  116. package/src/user-event/selection.ts +15 -0
  117. package/src/user-event/state.ts +2 -2
  118. package/src/user-event/text.ts +31 -4
  119. package/src/wait-for.ts +2 -0
  120. package/src/widget-accessible-properties.ts +188 -10
  121. package/src/within.ts +1 -1
@@ -0,0 +1,28 @@
1
+ import type { NormalizerFn, NormalizerOptions } from "./types.js";
2
+
3
+ /**
4
+ * Builds the default text normalizer, which optionally trims surrounding whitespace and collapses
5
+ * runs of whitespace into single spaces.
6
+ * @param options Toggles for trimming and whitespace collapsing.
7
+ * @returns A function that normalizes a string for comparison against a matcher.
8
+ */
9
+ const getDefaultNormalizer = ({
10
+ trim = true,
11
+ collapseWhitespace = true,
12
+ }: NormalizerOptions = {}): NormalizerFn => {
13
+ return (text: string): string => {
14
+ let result = text;
15
+
16
+ if (trim) {
17
+ result = result.trim();
18
+ }
19
+
20
+ if (collapseWhitespace) {
21
+ result = result.replaceAll(/\s+/g, " ");
22
+ }
23
+
24
+ return result;
25
+ };
26
+ };
27
+
28
+ export { getDefaultNormalizer };
@@ -4,6 +4,7 @@ import { formatRole } from "./role-helpers.js";
4
4
  import { type Container, roots } from "./traversal.js";
5
5
  import { getWidgetNodeText } from "./widget-accessible-properties.js";
6
6
 
7
+ /** Produces the value of the `id` attribute printed first on a widget's opening tag. */
7
8
  type WidgetIdResolver = (widget: Gtk.Widget) => string;
8
9
 
9
10
  /**
@@ -14,7 +15,7 @@ type PrettyWidgetOptions = {
14
15
  /** Truncates the output once it exceeds this many characters. */
15
16
  maxLength?: number;
16
17
  /** Whether to apply ANSI color highlighting; defaults to the terminal capabilities. */
17
- highlight?: boolean;
18
+ shouldHighlight?: boolean;
18
19
  /** Resolves an `id` attribute to show for each widget. */
19
20
  getId?: WidgetIdResolver;
20
21
  /** Stops descending past this depth, replacing deeper children with a summary line. */
@@ -76,7 +77,7 @@ const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [s
76
77
  return [...idAttrs, ...otherAttrs];
77
78
  };
78
79
 
79
- const shouldHighlight = (): boolean => {
80
+ const isHighlightSupported = (): boolean => {
80
81
  if (typeof process === "undefined") {
81
82
  return false;
82
83
  }
@@ -206,8 +207,8 @@ const prettyWidget = (container: Container, options: PrettyWidgetOptions = {}):
206
207
  return "";
207
208
  }
208
209
 
209
- const isHighlight = options.highlight ?? shouldHighlight();
210
- const colors = createColors(isHighlight);
210
+ const shouldHighlight = options.shouldHighlight ?? isHighlightSupported();
211
+ const colors = createColors(shouldHighlight);
211
212
  let output = "";
212
213
 
213
214
  for (const root of roots(container)) {
package/src/queries.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
- import type { ByRoleOptions, ByRoleValue, Matcher, MatcherOptions, NormalizerFn, NormalizerOptions } from "./types.js";
2
+ import type {
3
+ ByRoleOptions,
4
+ ByRoleValue,
5
+ Matcher,
6
+ MatcherOptions,
7
+ NormalizerFn,
8
+ QueryFamilies,
9
+ } from "./types.js";
3
10
  import { buildQueries, type BuiltQueries, type QueryAllBy } from "./build-queries.js";
4
11
  import { multipleFoundError, notFoundError } from "./errors.js";
12
+ import { getDefaultNormalizer } from "./normalize.js";
5
13
  import { type Container, findAll, traverse } from "./traversal.js";
6
14
  import {
7
15
  getWidgetAccessibleName,
8
16
  getWidgetBusyState,
9
- getWidgetCheckedState,
10
17
  getWidgetDescription,
11
18
  getWidgetDisplayValue,
12
19
  getWidgetExpandedState,
@@ -20,30 +27,11 @@ import {
20
27
  getWidgetSelectedState,
21
28
  getWidgetValue,
22
29
  isInaccessible,
30
+ isWidgetChecked,
23
31
  } from "./widget-accessible-properties.js";
24
32
 
25
- type QueryFamilyReturns = {
26
- queryBy: Gtk.Widget | null;
27
- queryAllBy: Gtk.Widget[];
28
- getBy: Gtk.Widget;
29
- getAllBy: Gtk.Widget[];
30
- findBy: Promise<Gtk.Widget>;
31
- findAllBy: Promise<Gtk.Widget[]>;
32
- };
33
-
34
- type NamedFamily<Suffix extends string, Args extends unknown[]> = {
35
- [K in keyof QueryFamilyReturns as `${K & string}${Suffix}`]: (
36
- container: Container,
37
- ...args: Args
38
- ) => QueryFamilyReturns[K];
39
- };
40
-
41
- type BuiltinQueries = NamedFamily<"Role", [role: Gtk.AccessibleRole, options?: ByRoleOptions]> &
42
- NamedFamily<"LabelText", [text: Matcher, options?: MatcherOptions]> &
43
- NamedFamily<"Text", [text: Matcher, options?: MatcherOptions]> &
44
- NamedFamily<"Name", [name: Matcher, options?: MatcherOptions]> &
45
- NamedFamily<"PlaceholderText", [text: Matcher, options?: MatcherOptions]> &
46
- NamedFamily<"DisplayValue", [value: Matcher, options?: MatcherOptions]>;
33
+ /** The built-in queries in their unbound form, each taking the container to search as its first argument. */
34
+ type BuiltinQueries = QueryFamilies<[container: Container]>;
47
35
 
48
36
  const roleQueries = nameQueryFamily(
49
37
  "Role",
@@ -112,167 +100,132 @@ const displayValueQueries = nameQueryFamily(
112
100
  ),
113
101
  );
114
102
 
115
- const builtinQueries: BuiltinQueries = {
103
+ const builtinQueries = {
116
104
  ...roleQueries,
117
105
  ...labelTextQueries,
118
106
  ...textQueries,
119
107
  ...nameQueries,
120
108
  ...placeholderTextQueries,
121
109
  ...displayValueQueries,
122
- };
110
+ } as BuiltinQueries;
123
111
 
124
112
  /**
125
113
  * Returns the single widget with a matching accessible role and options, or null when none match.
126
114
  * Throws when more than one matches.
127
115
  */
128
- const queryByRole: BuiltinQueries["queryByRole"] = roleQueries.queryByRole;
116
+ const queryByRole: BuiltinQueries["queryByRole"] = builtinQueries.queryByRole;
129
117
  /** Returns every widget with a matching accessible role and options. Throws when none match. */
130
- const getAllByRole: BuiltinQueries["getAllByRole"] = roleQueries.getAllByRole;
118
+ const getAllByRole: BuiltinQueries["getAllByRole"] = builtinQueries.getAllByRole;
131
119
  /**
132
120
  * Returns the single widget with a matching accessible role and options.
133
121
  * Throws when none or more than one matches.
134
122
  */
135
- const getByRole: BuiltinQueries["getByRole"] = roleQueries.getByRole;
123
+ const getByRole: BuiltinQueries["getByRole"] = builtinQueries.getByRole;
136
124
  /**
137
125
  * Waits for and returns every widget with a matching accessible role and options, retrying until at
138
126
  * least one appears or the timeout elapses.
139
127
  */
140
- const findAllByRole: BuiltinQueries["findAllByRole"] = roleQueries.findAllByRole;
128
+ const findAllByRole: BuiltinQueries["findAllByRole"] = builtinQueries.findAllByRole;
141
129
  /**
142
130
  * Waits for and returns the single widget with a matching accessible role and options, retrying
143
131
  * until it appears or the timeout elapses. Rejects when none or more than one matches.
144
132
  */
145
- const findByRole: BuiltinQueries["findByRole"] = roleQueries.findByRole;
133
+ const findByRole: BuiltinQueries["findByRole"] = builtinQueries.findByRole;
146
134
  /**
147
135
  * Returns the single widget with matching associated label text, or null when none match.
148
136
  * Throws when more than one matches.
149
137
  */
150
- const queryByLabelText: BuiltinQueries["queryByLabelText"] = labelTextQueries.queryByLabelText;
138
+ const queryByLabelText: BuiltinQueries["queryByLabelText"] = builtinQueries.queryByLabelText;
151
139
  /** Returns every widget with matching associated label text. Throws when none match. */
152
- const getAllByLabelText: BuiltinQueries["getAllByLabelText"] = labelTextQueries.getAllByLabelText;
140
+ const getAllByLabelText: BuiltinQueries["getAllByLabelText"] = builtinQueries.getAllByLabelText;
153
141
  /** Returns the single widget with matching associated label text. Throws when none or more than one matches. */
154
- const getByLabelText: BuiltinQueries["getByLabelText"] = labelTextQueries.getByLabelText;
142
+ const getByLabelText: BuiltinQueries["getByLabelText"] = builtinQueries.getByLabelText;
155
143
  /**
156
144
  * Waits for and returns every widget with matching associated label text, retrying until at least
157
145
  * one appears or the timeout elapses.
158
146
  */
159
- const findAllByLabelText: BuiltinQueries["findAllByLabelText"] = labelTextQueries.findAllByLabelText;
147
+ const findAllByLabelText: BuiltinQueries["findAllByLabelText"] = builtinQueries.findAllByLabelText;
160
148
  /**
161
149
  * Waits for and returns the single widget with matching associated label text, retrying until it
162
150
  * appears or the timeout elapses. Rejects when none or more than one matches.
163
151
  */
164
- const findByLabelText: BuiltinQueries["findByLabelText"] = labelTextQueries.findByLabelText;
152
+ const findByLabelText: BuiltinQueries["findByLabelText"] = builtinQueries.findByLabelText;
165
153
  /**
166
154
  * Returns the single widget with matching rendered text content, or null when none match.
167
155
  * Throws when more than one matches.
168
156
  */
169
- const queryByText: BuiltinQueries["queryByText"] = textQueries.queryByText;
157
+ const queryByText: BuiltinQueries["queryByText"] = builtinQueries.queryByText;
170
158
  /** Returns every widget with matching rendered text content. Throws when none match. */
171
- const getAllByText: BuiltinQueries["getAllByText"] = textQueries.getAllByText;
159
+ const getAllByText: BuiltinQueries["getAllByText"] = builtinQueries.getAllByText;
172
160
  /** Returns the single widget with matching rendered text content. Throws when none or more than one matches. */
173
- const getByText: BuiltinQueries["getByText"] = textQueries.getByText;
161
+ const getByText: BuiltinQueries["getByText"] = builtinQueries.getByText;
174
162
  /**
175
163
  * Waits for and returns every widget with matching rendered text content, retrying until at least
176
164
  * one appears or the timeout elapses.
177
165
  */
178
- const findAllByText: BuiltinQueries["findAllByText"] = textQueries.findAllByText;
166
+ const findAllByText: BuiltinQueries["findAllByText"] = builtinQueries.findAllByText;
179
167
  /**
180
168
  * Waits for and returns the single widget with matching rendered text content, retrying until it
181
169
  * appears or the timeout elapses. Rejects when none or more than one matches.
182
170
  */
183
- const findByText: BuiltinQueries["findByText"] = textQueries.findByText;
171
+ const findByText: BuiltinQueries["findByText"] = builtinQueries.findByText;
184
172
  /**
185
173
  * Returns the single widget with a matching widget name, or null when none match.
186
174
  * Throws when more than one matches.
187
175
  */
188
- const queryByName: BuiltinQueries["queryByName"] = nameQueries.queryByName;
176
+ const queryByName: BuiltinQueries["queryByName"] = builtinQueries.queryByName;
189
177
  /** Returns every widget with a matching widget name. Throws when none match. */
190
- const getAllByName: BuiltinQueries["getAllByName"] = nameQueries.getAllByName;
178
+ const getAllByName: BuiltinQueries["getAllByName"] = builtinQueries.getAllByName;
191
179
  /** Returns the single widget with a matching widget name. Throws when none or more than one matches. */
192
- const getByName: BuiltinQueries["getByName"] = nameQueries.getByName;
180
+ const getByName: BuiltinQueries["getByName"] = builtinQueries.getByName;
193
181
  /**
194
182
  * Waits for and returns every widget with a matching widget name, retrying until at least one
195
183
  * appears or the timeout elapses.
196
184
  */
197
- const findAllByName: BuiltinQueries["findAllByName"] = nameQueries.findAllByName;
185
+ const findAllByName: BuiltinQueries["findAllByName"] = builtinQueries.findAllByName;
198
186
  /**
199
187
  * Waits for and returns the single widget with a matching widget name, retrying until it appears or
200
188
  * the timeout elapses. Rejects when none or more than one matches.
201
189
  */
202
- const findByName: BuiltinQueries["findByName"] = nameQueries.findByName;
203
-
190
+ const findByName: BuiltinQueries["findByName"] = builtinQueries.findByName;
204
191
  /**
205
192
  * Returns the single widget with matching placeholder text, or null when none match.
206
193
  * Throws when more than one matches.
207
194
  */
208
- const queryByPlaceholderText: BuiltinQueries["queryByPlaceholderText"] =
209
- placeholderTextQueries.queryByPlaceholderText;
210
-
195
+ const queryByPlaceholderText: BuiltinQueries["queryByPlaceholderText"] = builtinQueries.queryByPlaceholderText;
211
196
  /** Returns every widget with matching placeholder text. Throws when none match. */
212
- const getAllByPlaceholderText: BuiltinQueries["getAllByPlaceholderText"] =
213
- placeholderTextQueries.getAllByPlaceholderText;
214
-
197
+ const getAllByPlaceholderText: BuiltinQueries["getAllByPlaceholderText"] = builtinQueries.getAllByPlaceholderText;
215
198
  /** Returns the single widget with matching placeholder text. Throws when none or more than one matches. */
216
- const getByPlaceholderText: BuiltinQueries["getByPlaceholderText"] = placeholderTextQueries.getByPlaceholderText;
217
-
199
+ const getByPlaceholderText: BuiltinQueries["getByPlaceholderText"] = builtinQueries.getByPlaceholderText;
218
200
  /**
219
201
  * Waits for and returns every widget with matching placeholder text, retrying until at least one
220
202
  * appears or the timeout elapses.
221
203
  */
222
- const findAllByPlaceholderText: BuiltinQueries["findAllByPlaceholderText"] =
223
- placeholderTextQueries.findAllByPlaceholderText;
224
-
204
+ const findAllByPlaceholderText: BuiltinQueries["findAllByPlaceholderText"] = builtinQueries.findAllByPlaceholderText;
225
205
  /**
226
206
  * Waits for and returns the single widget with matching placeholder text, retrying until it appears
227
207
  * or the timeout elapses. Rejects when none or more than one matches.
228
208
  */
229
- const findByPlaceholderText: BuiltinQueries["findByPlaceholderText"] =
230
- placeholderTextQueries.findByPlaceholderText;
231
-
209
+ const findByPlaceholderText: BuiltinQueries["findByPlaceholderText"] = builtinQueries.findByPlaceholderText;
232
210
  /**
233
211
  * Returns the single widget with a matching display value, or null when none match.
234
212
  * Throws when more than one matches.
235
213
  */
236
- const queryByDisplayValue: BuiltinQueries["queryByDisplayValue"] = displayValueQueries.queryByDisplayValue;
214
+ const queryByDisplayValue: BuiltinQueries["queryByDisplayValue"] = builtinQueries.queryByDisplayValue;
237
215
  /** Returns every widget with a matching display value. Throws when none match. */
238
- const getAllByDisplayValue: BuiltinQueries["getAllByDisplayValue"] = displayValueQueries.getAllByDisplayValue;
216
+ const getAllByDisplayValue: BuiltinQueries["getAllByDisplayValue"] = builtinQueries.getAllByDisplayValue;
239
217
  /** Returns the single widget with a matching display value. Throws when none or more than one matches. */
240
- const getByDisplayValue: BuiltinQueries["getByDisplayValue"] = displayValueQueries.getByDisplayValue;
218
+ const getByDisplayValue: BuiltinQueries["getByDisplayValue"] = builtinQueries.getByDisplayValue;
241
219
  /**
242
220
  * Waits for and returns every widget with a matching display value, retrying until at least one
243
221
  * appears or the timeout elapses.
244
222
  */
245
- const findAllByDisplayValue: BuiltinQueries["findAllByDisplayValue"] = displayValueQueries.findAllByDisplayValue;
223
+ const findAllByDisplayValue: BuiltinQueries["findAllByDisplayValue"] = builtinQueries.findAllByDisplayValue;
246
224
  /**
247
225
  * Waits for and returns the single widget with a matching display value, retrying until it appears
248
226
  * or the timeout elapses. Rejects when none or more than one matches.
249
227
  */
250
- const findByDisplayValue: BuiltinQueries["findByDisplayValue"] = displayValueQueries.findByDisplayValue;
251
-
252
- /**
253
- * Builds the default text normalizer, which optionally trims surrounding whitespace and collapses
254
- * runs of whitespace into single spaces.
255
- * @param options Toggles for trimming and whitespace collapsing.
256
- * @returns A function that normalizes a string for comparison against a matcher.
257
- */
258
- const getDefaultNormalizer = ({
259
- trim = true,
260
- collapseWhitespace = true,
261
- }: NormalizerOptions = {}): NormalizerFn => {
262
- return (text: string): string => {
263
- let result = text;
264
-
265
- if (trim) {
266
- result = result.trim();
267
- }
268
-
269
- if (collapseWhitespace) {
270
- result = result.replaceAll(/\s+/g, " ");
271
- }
272
-
273
- return result;
274
- };
275
- };
228
+ const findByDisplayValue: BuiltinQueries["findByDisplayValue"] = builtinQueries.findByDisplayValue;
276
229
 
277
230
  const buildNormalizer = (options?: MatcherOptions): NormalizerFn => {
278
231
  const { normalizer, trim, collapseWhitespace } = options ?? {};
@@ -361,7 +314,7 @@ const hasMatchingAccessibleValue = (widget: Gtk.Widget, value: ByRoleValue, opti
361
314
 
362
315
  const hasMatchingBooleanStates = (widget: Gtk.Widget, options: ByRoleOptions): boolean => {
363
316
  const stateChecks: [boolean | undefined, () => boolean | null][] = [
364
- [options.checked, () => getWidgetCheckedState(widget)],
317
+ [options.checked, () => isWidgetChecked(widget)],
365
318
  [options.pressed, () => getWidgetPressedState(widget)],
366
319
  [options.expanded, () => getWidgetExpandedState(widget)],
367
320
  [options.selected, () => getWidgetSelectedState(widget)],
@@ -393,20 +346,27 @@ const hasMatchingAccessibleStates = (widget: Gtk.Widget, options: ByRoleOptions)
393
346
  hasMatchingDescriptionState(widget, options) &&
394
347
  hasMatchingValueState(widget, options);
395
348
 
349
+ const isMatchingWidgetType = (widget: Gtk.Widget, options?: MatcherOptions): boolean =>
350
+ options?.as === undefined || widget instanceof options.as;
351
+
396
352
  const hasMatchingByRoleOptions = (widget: Gtk.Widget, options?: ByRoleOptions): boolean => {
397
353
  if (!options) {
398
354
  return true;
399
355
  }
400
356
 
401
- return hasMatchingAccessibleName(widget, options) && hasMatchingAccessibleStates(widget, options);
357
+ return (
358
+ isMatchingWidgetType(widget, options) &&
359
+ hasMatchingAccessibleName(widget, options) &&
360
+ hasMatchingAccessibleStates(widget, options)
361
+ );
402
362
  };
403
363
 
404
- function nameQueryFamily<Suffix extends string, Args extends unknown[]>(
405
- suffix: Suffix,
364
+ function nameQueryFamily<Args extends unknown[]>(
365
+ suffix: string,
406
366
  queryAllBy: QueryAllBy<Args>,
407
367
  built: BuiltQueries<Args>,
408
- ): NamedFamily<Suffix, Args> {
409
- const family = {
368
+ ): Record<string, unknown> {
369
+ return {
410
370
  [`queryBy${suffix}`]: built.queryBy,
411
371
  [`queryAllBy${suffix}`]: queryAllBy,
412
372
  [`getBy${suffix}`]: built.getBy,
@@ -414,8 +374,6 @@ function nameQueryFamily<Suffix extends string, Args extends unknown[]>(
414
374
  [`findBy${suffix}`]: built.findBy,
415
375
  [`findAllBy${suffix}`]: built.findAllBy,
416
376
  };
417
-
418
- return family as NamedFamily<Suffix, Args>;
419
377
  }
420
378
 
421
379
  /**
@@ -502,7 +460,7 @@ function queryAllByLabelText(container: Container, text: Matcher, options?: Matc
502
460
  collectLabelMatches(results, widget, text, options);
503
461
  }
504
462
 
505
- return [...results];
463
+ return [...results].filter((widget) => isMatchingWidgetType(widget, options));
506
464
  }
507
465
 
508
466
  /**
@@ -513,7 +471,11 @@ function queryAllByLabelText(container: Container, text: Matcher, options?: Matc
513
471
  * @returns Every matching widget, or an empty array when none match.
514
472
  */
515
473
  function queryAllByText(container: Container, text: Matcher, options?: MatcherOptions): Gtk.Widget[] {
516
- return findAll(container, (widget) => isTextMatch(getWidgetLabelText(widget), text, widget, options));
474
+ return findAll(
475
+ container,
476
+ (widget) =>
477
+ isMatchingWidgetType(widget, options) && isTextMatch(getWidgetLabelText(widget), text, widget, options),
478
+ );
517
479
  }
518
480
 
519
481
  /**
@@ -524,7 +486,10 @@ function queryAllByText(container: Container, text: Matcher, options?: MatcherOp
524
486
  * @returns Every matching widget, or an empty array when none match.
525
487
  */
526
488
  function queryAllByName(container: Container, name: Matcher, options?: MatcherOptions): Gtk.Widget[] {
527
- return findAll(container, (widget) => isTextMatch(getWidgetName(widget), name, widget, options));
489
+ return findAll(
490
+ container,
491
+ (widget) => isMatchingWidgetType(widget, options) && isTextMatch(getWidgetName(widget), name, widget, options),
492
+ );
528
493
  }
529
494
 
530
495
  /**
@@ -539,7 +504,12 @@ function queryAllByPlaceholderText(
539
504
  text: Matcher,
540
505
  options?: MatcherOptions,
541
506
  ): Gtk.Widget[] {
542
- return findAll(container, (widget) => isTextMatch(getWidgetPlaceholderText(widget), text, widget, options));
507
+ return findAll(
508
+ container,
509
+ (widget) =>
510
+ isMatchingWidgetType(widget, options) &&
511
+ isTextMatch(getWidgetPlaceholderText(widget), text, widget, options),
512
+ );
543
513
  }
544
514
 
545
515
  /**
@@ -550,7 +520,12 @@ function queryAllByPlaceholderText(
550
520
  * @returns Every matching widget, or an empty array when none match.
551
521
  */
552
522
  function queryAllByDisplayValue(container: Container, value: Matcher, options?: MatcherOptions): Gtk.Widget[] {
553
- return findAll(container, (widget) => isTextMatch(getWidgetDisplayValue(widget), value, widget, options));
523
+ return findAll(
524
+ container,
525
+ (widget) =>
526
+ isMatchingWidgetType(widget, options) &&
527
+ isTextMatch(getWidgetDisplayValue(widget), value, widget, options),
528
+ );
554
529
  }
555
530
 
556
531
  export {
@@ -585,7 +560,6 @@ export {
585
560
  getByDisplayValue,
586
561
  findAllByDisplayValue,
587
562
  findByDisplayValue,
588
- getDefaultNormalizer,
589
563
  queryAllByRole,
590
564
  queryAllByLabelText,
591
565
  queryAllByText,
package/src/render.tsx CHANGED
@@ -97,7 +97,7 @@ const resolveContainer = (container: RenderOptions["container"]): ResolvedContai
97
97
  }
98
98
 
99
99
  const window = new Gtk.Window({ defaultWidth: HARNESS_WINDOW_WIDTH, defaultHeight: HARNESS_WINDOW_HEIGHT });
100
- window.setTitlebar(new Gtk.HeaderBar({ showTitleButtons: false }));
100
+ window.setDecorated(false);
101
101
 
102
102
  return { containerInfo: window, window };
103
103
  };
@@ -165,7 +165,7 @@ const render = async <Q extends QueryMap = Record<never, never>>(
165
165
  options?: RenderOptions<Q>,
166
166
  ): Promise<RenderResult<Q>> => {
167
167
  installErrorHandler();
168
- applyEnableAnimations(options?.animations === true);
168
+ applyEnableAnimations(options?.areAnimationsEnabled === true);
169
169
  const baseElement: Container = options?.baseElement ?? TOPLEVELS;
170
170
  const Wrapper = options?.wrapper;
171
171
  const resolved = resolveContainer(options?.container);
@@ -184,7 +184,7 @@ const render = async <Q extends QueryMap = Record<never, never>>(
184
184
  const wrap = (node: ReactNode): ReactNode => {
185
185
  const wrapped = Wrapper ? <Wrapper>{node}</Wrapper> : node;
186
186
 
187
- return options?.reactStrictMode ? <StrictMode>{wrapped}</StrictMode> : wrapped;
187
+ return options?.isReactStrictMode ? <StrictMode>{wrapped}</StrictMode> : wrapped;
188
188
  };
189
189
 
190
190
  await update(wrap(element), root);
@@ -20,10 +20,13 @@ type Method = "Role" | "LabelText" | "PlaceholderText" | "Text" | "DisplayValue"
20
20
  * `toString` that renders the full call.
21
21
  */
22
22
  type Suggestion = {
23
+ /** The query family, such as `Role`. */
23
24
  queryName: Method;
24
25
  /** The full query function name, such as `getByRole`. */
25
26
  queryMethod: string;
27
+ /** The variant the suggestion was asked for, which prefixes `Suggestion.queryMethod`. */
26
28
  variant: Variant;
29
+ /** Renders the suggested call with its arguments, such as `getByRole(Gtk.AccessibleRole.BUTTON)`. */
27
30
  toString: () => string;
28
31
  };
29
32
 
package/src/traversal.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
2
 
3
+ /**
4
+ * A scope that resolves to a single root widget: the widget itself, the widget a controller or
5
+ * layout manager is attached to, or a list item's or header's child.
6
+ */
3
7
  type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;
4
8
  /**
5
9
  * A scope that queries and traversal can run against: a widget, an event
@@ -8,6 +12,7 @@ type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk
8
12
  */
9
13
  type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;
10
14
 
15
+ /** Container sentinel that widens a query to every toplevel window currently open. */
11
16
  const TOPLEVELS: unique symbol = Symbol("gtkx.toplevels");
12
17
 
13
18
  const isApplication = (container: Container): container is Gtk.Application => container instanceof Gtk.Application;