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

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 (44) hide show
  1. package/dist/bound-queries.d.ts +2 -3
  2. package/dist/bound-queries.d.ts.map +1 -1
  3. package/dist/bound-queries.js.map +1 -1
  4. package/dist/index.d.ts +7 -4
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/matchers.d.ts +81 -21
  9. package/dist/matchers.d.ts.map +1 -1
  10. package/dist/matchers.js +258 -21
  11. package/dist/matchers.js.map +1 -1
  12. package/dist/normalize.d.ts +10 -0
  13. package/dist/normalize.d.ts.map +1 -0
  14. package/dist/normalize.js +20 -0
  15. package/dist/normalize.js.map +1 -0
  16. package/dist/queries.d.ts +3 -21
  17. package/dist/queries.d.ts.map +1 -1
  18. package/dist/queries.js +46 -59
  19. package/dist/queries.js.map +1 -1
  20. package/dist/render.js +1 -1
  21. package/dist/render.js.map +1 -1
  22. package/dist/types.d.ts +29 -3
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/types.js.map +1 -1
  25. package/dist/user-event/controller.d.ts +2 -1
  26. package/dist/user-event/controller.d.ts.map +1 -1
  27. package/dist/user-event/controller.js +8 -1
  28. package/dist/user-event/controller.js.map +1 -1
  29. package/dist/widget-accessible-properties.d.ts +12 -2
  30. package/dist/widget-accessible-properties.d.ts.map +1 -1
  31. package/dist/widget-accessible-properties.js +112 -10
  32. package/dist/widget-accessible-properties.js.map +1 -1
  33. package/dist/within.js.map +1 -1
  34. package/package.json +6 -6
  35. package/src/bound-queries.ts +2 -3
  36. package/src/index.ts +11 -2
  37. package/src/matchers.ts +456 -53
  38. package/src/normalize.ts +28 -0
  39. package/src/queries.ts +78 -105
  40. package/src/render.tsx +1 -1
  41. package/src/types.ts +48 -2
  42. package/src/user-event/controller.ts +21 -1
  43. package/src/widget-accessible-properties.ts +172 -10
  44. package/src/within.ts +1 -1
@@ -11,9 +11,19 @@ type WidgetValue = {
11
11
  };
12
12
 
13
13
  type ValueTriplet = { now: number | null; min: number | null; max: number | null };
14
+ /** A widget's checked state: on, off, or the mixed state of an inconsistent check button. */
15
+ type CheckedState = "checked" | "unchecked" | "mixed";
14
16
 
15
17
  const DEFAULT_TEXT_GETTERS = ["getLabel", "getText", "getTitle"] as const;
16
18
 
19
+ const SELECTABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
20
+ Gtk.AccessibleRole.ROW,
21
+ Gtk.AccessibleRole.LIST_ITEM,
22
+ Gtk.AccessibleRole.GRID_CELL,
23
+ Gtk.AccessibleRole.OPTION,
24
+ Gtk.AccessibleRole.TREE_ITEM,
25
+ ]);
26
+
17
27
  const callStringGetter = (widget: Gtk.Widget, method: string): string | null => {
18
28
  const fn: unknown = Reflect.get(widget, method);
19
29
 
@@ -129,6 +139,49 @@ const getWidgetLabelText = (widget: Gtk.Widget): string | null => {
129
139
  return getLabelText(widget);
130
140
  };
131
141
 
142
+ const getChildren = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {
143
+ let child = widget.getFirstChild();
144
+
145
+ while (child) {
146
+ yield child;
147
+ child = child.getNextSibling();
148
+ }
149
+ };
150
+
151
+ const textContentParts = (widget: Gtk.Widget): string[] => {
152
+ const own = getWidgetNodeText(widget);
153
+
154
+ if (own !== null) {
155
+ return [own];
156
+ }
157
+
158
+ return [...getChildren(widget)].flatMap((child) => textContentParts(child));
159
+ };
160
+
161
+ const getWidgetTextContent = (widget: Gtk.Widget): string | null => {
162
+ const parts = textContentParts(widget);
163
+
164
+ return parts.length > 0 ? parts.join(" ") : null;
165
+ };
166
+
167
+ const isComboBoxFaceCandidate = (widget: Gtk.Widget): boolean =>
168
+ !(widget instanceof Gtk.Popover) && widget.getChildVisible();
169
+
170
+ const comboBoxChildFaceText = (child: Gtk.Widget): string | null =>
171
+ isComboBoxFaceCandidate(child) ? getWidgetLabelText(child) ?? comboBoxFaceText(child) : null;
172
+
173
+ const comboBoxFaceText = (widget: Gtk.Widget): string | null => {
174
+ for (const child of getChildren(widget)) {
175
+ const text = comboBoxChildFaceText(child);
176
+
177
+ if (text !== null) {
178
+ return text;
179
+ }
180
+ }
181
+
182
+ return null;
183
+ };
184
+
132
185
  const tabPanelTitle = (widget: Gtk.Widget): string | null => {
133
186
  const parent = widget.getParent();
134
187
 
@@ -146,6 +199,12 @@ const getWidgetAccessibleName = (widget: Gtk.Widget): string | null => {
146
199
  return tabPanelTitle(widget);
147
200
  }
148
201
 
202
+ const labelledByText = getWidgetLabelledByText(widget);
203
+
204
+ if (labelledByText) {
205
+ return labelledByText;
206
+ }
207
+
149
208
  const accessibleLabel = readAccessibleString(widget, "accessibleLabel");
150
209
 
151
210
  if (accessibleLabel) {
@@ -176,7 +235,15 @@ const getWidgetPlaceholderText = (widget: Gtk.Widget): string | null => {
176
235
  return null;
177
236
  }
178
237
 
179
- return callStringGetter(widget, "getPlaceholderText");
238
+ const fromGetter = callStringGetter(widget, "getPlaceholderText");
239
+
240
+ if (fromGetter !== null) {
241
+ return fromGetter;
242
+ }
243
+
244
+ const fromAccessor: unknown = Reflect.get(widget, "placeholderText");
245
+
246
+ return typeof fromAccessor === "string" && fromAccessor !== "" ? fromAccessor : null;
180
247
  };
181
248
 
182
249
  const getWidgetDisplayValue = (widget: Gtk.Widget): string | null => {
@@ -184,25 +251,89 @@ const getWidgetDisplayValue = (widget: Gtk.Widget): string | null => {
184
251
  return readEditableText(widget);
185
252
  }
186
253
 
254
+ if (widget.getAccessibleRole() === Gtk.AccessibleRole.COMBO_BOX) {
255
+ return comboBoxFaceText(widget);
256
+ }
257
+
187
258
  return null;
188
259
  };
189
260
 
190
- const getWidgetCheckedState = (widget: Gtk.Widget): boolean | null => {
191
- if (widget instanceof Gtk.CheckButton) {
192
- return widget.getActive();
261
+ const hasDisplayValue = (widget: Gtk.Widget): boolean => {
262
+ const role = widget.getAccessibleRole();
263
+
264
+ return (EDITABLE_ROLES.has(role) && isEditable(widget)) || role === Gtk.AccessibleRole.COMBO_BOX;
265
+ };
266
+
267
+ const isWidgetDisabled = (widget: Gtk.Widget): boolean => !widget.isSensitive();
268
+
269
+ const hasZeroOpacityAncestor = (widget: Gtk.Widget): boolean => {
270
+ let current: Gtk.Widget | null = widget;
271
+
272
+ while (current) {
273
+ if (current.getOpacity() === 0) {
274
+ return true;
275
+ }
276
+
277
+ current = current.getParent();
193
278
  }
194
279
 
195
- if (widget instanceof Gtk.Switch) {
196
- return widget.getActive();
280
+ return false;
281
+ };
282
+
283
+ const isWidgetVisible = (widget: Gtk.Widget): boolean => widget.isVisible() && !hasZeroOpacityAncestor(widget);
284
+
285
+ const getWidgetRequiredState = (widget: Gtk.Widget): boolean | null =>
286
+ readAccessibleBoolean(widget, "accessibleRequired");
287
+
288
+ const readTextViewSelection = (view: Gtk.TextView): string | null => {
289
+ const buffer = view.getBuffer();
290
+ const [hasSelection, start, end] = buffer.getSelectionBounds();
291
+
292
+ return hasSelection ? buffer.getText(start, end, true) : null;
293
+ };
294
+
295
+ const readEditableSelection = (editable: Gtk.Editable): string | null => {
296
+ const [hasSelection, start, end] = editable.getSelectionBounds();
297
+
298
+ return hasSelection ? editable.getChars(start, end) : null;
299
+ };
300
+
301
+ const getWidgetSelection = (widget: Gtk.Widget): string | null => {
302
+ if (widget instanceof Gtk.TextView) {
303
+ return readTextViewSelection(widget);
197
304
  }
198
305
 
199
- if (widget instanceof Gtk.ToggleButton && widget.getAccessibleRole() === Gtk.AccessibleRole.RADIO) {
200
- return widget.getActive();
306
+ if (widget instanceof Gtk.Editable) {
307
+ return readEditableSelection(widget);
308
+ }
309
+
310
+ return null;
311
+ };
312
+
313
+ const checkedFromActive = (isActive: boolean): CheckedState => (isActive ? "checked" : "unchecked");
314
+
315
+ const isCheckableToggle = (widget: Gtk.Widget): boolean =>
316
+ widget instanceof Gtk.Switch ||
317
+ (widget instanceof Gtk.ToggleButton && widget.getAccessibleRole() === Gtk.AccessibleRole.RADIO);
318
+
319
+ const getWidgetCheckedState = (widget: Gtk.Widget): CheckedState | null => {
320
+ if (widget instanceof Gtk.CheckButton) {
321
+ return widget.getInconsistent() ? "mixed" : checkedFromActive(widget.getActive());
322
+ }
323
+
324
+ if (isCheckableToggle(widget)) {
325
+ return checkedFromActive(Reflect.get(widget, "active") === true);
201
326
  }
202
327
 
203
328
  return null;
204
329
  };
205
330
 
331
+ const isWidgetChecked = (widget: Gtk.Widget): boolean | null => {
332
+ const state = getWidgetCheckedState(widget);
333
+
334
+ return state === null || state === "mixed" ? null : state === "checked";
335
+ };
336
+
206
337
  const getWidgetPressedState = (widget: Gtk.Widget): boolean | null => {
207
338
  if (widget instanceof Gtk.ToggleButton) {
208
339
  return widget.getActive();
@@ -224,11 +355,11 @@ const getWidgetExpandedState = (widget: Gtk.Widget): boolean | null => {
224
355
  };
225
356
 
226
357
  const getWidgetSelectedState = (widget: Gtk.Widget): boolean | null => {
227
- if (widget instanceof Gtk.ListBoxRow) {
358
+ if (widget instanceof Gtk.ListBoxRow || widget instanceof Gtk.FlowBoxChild) {
228
359
  return widget.isSelected();
229
360
  }
230
361
 
231
- if (widget.getAccessibleRole() === Gtk.AccessibleRole.ROW) {
362
+ if (SELECTABLE_ROLES.has(widget.getAccessibleRole())) {
232
363
  return (widget.getStateFlags() & Gtk.StateFlags.SELECTED) !== 0;
233
364
  }
234
365
 
@@ -346,6 +477,28 @@ const getWidgetLabelledByText = (widget: Gtk.Widget): string | null => {
346
477
  return texts.length > 0 ? texts.join(" ") : null;
347
478
  };
348
479
 
480
+ const isTooltipUsedAsName = (widget: Gtk.Widget): boolean =>
481
+ readAccessibleString(widget, "accessibleLabel") === null &&
482
+ getWidgetNodeText(widget) === null &&
483
+ collectLabels(widget).length === 0;
484
+
485
+ const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
486
+ const targets = readAccessibleWidgets(widget, "accessibleDescribedBy");
487
+ const texts = targets === null ? [] : collectAccessibleNames(targets);
488
+
489
+ if (texts.length > 0) {
490
+ return texts.join(" ");
491
+ }
492
+
493
+ const description = readAccessibleString(widget, "accessibleDescription");
494
+
495
+ if (description !== null) {
496
+ return description;
497
+ }
498
+
499
+ return isTooltipUsedAsName(widget) ? null : callStringGetter(widget, "getTooltipText");
500
+ };
501
+
349
502
  const isInaccessible = (widget: Gtk.Widget): boolean => {
350
503
  let current: Gtk.Widget | null = widget;
351
504
 
@@ -366,12 +519,20 @@ const isInaccessible = (widget: Gtk.Widget): boolean => {
366
519
 
367
520
  export {
368
521
  getWidgetNodeText,
522
+ getWidgetTextContent,
369
523
  getWidgetLabelText,
370
524
  getWidgetAccessibleName,
371
525
  getWidgetName,
372
526
  getWidgetPlaceholderText,
373
527
  getWidgetDisplayValue,
528
+ hasDisplayValue,
529
+ isWidgetDisabled,
530
+ isWidgetVisible,
531
+ getWidgetRequiredState,
532
+ getWidgetSelection,
533
+ getWidgetDescribedByText,
374
534
  getWidgetCheckedState,
535
+ isWidgetChecked,
375
536
  getWidgetPressedState,
376
537
  getWidgetExpandedState,
377
538
  getWidgetSelectedState,
@@ -384,5 +545,6 @@ export {
384
545
  getWidgetOwnLabel,
385
546
  getWidgetLabelledByText,
386
547
  isInaccessible,
548
+ type CheckedState,
387
549
  type WidgetValue,
388
550
  };
package/src/within.ts CHANGED
@@ -26,7 +26,7 @@ const within = <Q extends QueryMap = Record<never, never>>(
26
26
  container: Container,
27
27
  queries?: Q,
28
28
  ): BoundQueries & BoundCustomQueries<Q> => ({
29
- ...bindCustomQueries(builtinQueries, container),
29
+ ...(bindCustomQueries(builtinQueries, container) as BoundQueries),
30
30
  ...(queries ? bindCustomQueries(queries, container) : ({} as BoundCustomQueries<Q>)),
31
31
  });
32
32