@gtkx/testing 2.0.0-beta.3 → 2.0.0-beta.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 (88) hide show
  1. package/dist/build-queries.d.ts +9 -9
  2. package/dist/build-queries.d.ts.map +1 -1
  3. package/dist/build-queries.js +2 -1
  4. package/dist/build-queries.js.map +1 -1
  5. package/dist/fire-event.d.ts +3 -3
  6. package/dist/fire-event.d.ts.map +1 -1
  7. package/dist/fire-event.js +14 -4
  8. package/dist/fire-event.js.map +1 -1
  9. package/dist/matchers.d.ts +6 -6
  10. package/dist/matchers.d.ts.map +1 -1
  11. package/dist/matchers.js +3 -2
  12. package/dist/matchers.js.map +1 -1
  13. package/dist/queries.d.ts +7 -7
  14. package/dist/queries.d.ts.map +1 -1
  15. package/dist/queries.js +16 -9
  16. package/dist/queries.js.map +1 -1
  17. package/dist/query-helpers.d.ts +2 -2
  18. package/dist/query-helpers.d.ts.map +1 -1
  19. package/dist/query-helpers.js +2 -1
  20. package/dist/query-helpers.js.map +1 -1
  21. package/dist/render.d.ts.map +1 -1
  22. package/dist/render.js +4 -0
  23. package/dist/render.js.map +1 -1
  24. package/dist/role-helpers.d.ts +1 -1
  25. package/dist/role-helpers.d.ts.map +1 -1
  26. package/dist/role-helpers.js +2 -1
  27. package/dist/role-helpers.js.map +1 -1
  28. package/dist/screenshot.d.ts +1 -1
  29. package/dist/screenshot.d.ts.map +1 -1
  30. package/dist/screenshot.js +3 -1
  31. package/dist/screenshot.js.map +1 -1
  32. package/dist/suggestions.d.ts +1 -1
  33. package/dist/suggestions.d.ts.map +1 -1
  34. package/dist/suggestions.js +3 -1
  35. package/dist/suggestions.js.map +1 -1
  36. package/dist/traversal.d.ts +3 -3
  37. package/dist/traversal.d.ts.map +1 -1
  38. package/dist/traversal.js +1 -1
  39. package/dist/traversal.js.map +1 -1
  40. package/dist/types.d.ts +8 -8
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js.map +1 -1
  43. package/dist/user-event/controller.d.ts +5 -5
  44. package/dist/user-event/controller.d.ts.map +1 -1
  45. package/dist/user-event/controller.js +5 -2
  46. package/dist/user-event/controller.js.map +1 -1
  47. package/dist/user-event/index.d.ts +22 -22
  48. package/dist/user-event/index.d.ts.map +1 -1
  49. package/dist/user-event/index.js +28 -25
  50. package/dist/user-event/index.js.map +1 -1
  51. package/dist/user-event/selection.d.ts +2 -2
  52. package/dist/user-event/selection.d.ts.map +1 -1
  53. package/dist/user-event/selection.js +8 -2
  54. package/dist/user-event/selection.js.map +1 -1
  55. package/dist/wait-for.d.ts +2 -2
  56. package/dist/wait-for.d.ts.map +1 -1
  57. package/dist/wait-for.js +6 -4
  58. package/dist/wait-for.js.map +1 -1
  59. package/dist/widget-accessible-properties.d.ts +2 -2
  60. package/dist/widget-accessible-properties.d.ts.map +1 -1
  61. package/dist/widget-accessible-properties.js +3 -1
  62. package/dist/widget-accessible-properties.js.map +1 -1
  63. package/dist/widget-getters.d.ts +7 -9
  64. package/dist/widget-getters.d.ts.map +1 -1
  65. package/dist/widget-getters.js.map +1 -1
  66. package/dist/widget-target.d.ts +4 -0
  67. package/dist/widget-target.d.ts.map +1 -0
  68. package/dist/widget-target.js +9 -0
  69. package/dist/widget-target.js.map +1 -0
  70. package/package.json +5 -5
  71. package/src/build-queries.ts +60 -30
  72. package/src/fire-event.ts +32 -8
  73. package/src/matchers.ts +13 -12
  74. package/src/queries.ts +61 -29
  75. package/src/query-helpers.ts +9 -7
  76. package/src/render.tsx +5 -0
  77. package/src/role-helpers.ts +3 -1
  78. package/src/screenshot.ts +3 -1
  79. package/src/suggestions.ts +3 -1
  80. package/src/traversal.ts +4 -4
  81. package/src/types.ts +12 -8
  82. package/src/user-event/controller.ts +8 -5
  83. package/src/user-event/index.ts +133 -47
  84. package/src/user-event/selection.ts +12 -4
  85. package/src/wait-for.ts +9 -6
  86. package/src/widget-accessible-properties.ts +7 -5
  87. package/src/widget-getters.ts +11 -13
  88. package/src/widget-target.ts +11 -0
package/src/screenshot.ts CHANGED
@@ -9,6 +9,7 @@ import { getConfig } from "./config.js";
9
9
  import { now } from "./timers.js";
10
10
  import { descendants } from "./traversal.js";
11
11
  import { waitFor } from "./wait-for.js";
12
+ import { requireWidget } from "./widget-target.js";
12
13
  import { activeToplevel, mappedToplevels } from "./window-state.js";
13
14
 
14
15
  type FrameProbe = {
@@ -301,7 +302,8 @@ const captureUntilPaintable = (
301
302
  * @param options Optional scale, timeout, retry interval, and output path.
302
303
  * @returns The base64-encoded PNG data along with its mime type and dimensions.
303
304
  */
304
- const screenshot = async (widget: Gtk.Widget, options?: ScreenshotOptions): Promise<ScreenshotResult> => {
305
+ const screenshot = async (target: Gtk.Accessible, options?: ScreenshotOptions): Promise<ScreenshotResult> => {
306
+ const widget = requireWidget(target);
305
307
  const scale = options?.scale ?? 1;
306
308
 
307
309
  if (!Number.isFinite(scale) || scale <= 0) {
@@ -9,6 +9,7 @@ import {
9
9
  getWidgetOwnLabel,
10
10
  getWidgetPlaceholderText,
11
11
  } from "./widget-accessible-properties.js";
12
+ import { requireWidget } from "./widget-target.js";
12
13
 
13
14
  /** The query variant a suggestion targets. */
14
15
  type Variant = "get" | "getAll" | "query" | "queryAll" | "find" | "findAll";
@@ -75,10 +76,11 @@ const textSuggestion = (queryName: Method, variant: Variant, value: string | nul
75
76
  * @returns The best available suggestion, or undefined when no query applies.
76
77
  */
77
78
  const getSuggestedQuery = (
78
- widget: Gtk.Widget,
79
+ target: Gtk.Accessible,
79
80
  variant: Variant = "get",
80
81
  method?: Method,
81
82
  ): Suggestion | undefined => {
83
+ const widget = requireWidget(target);
82
84
  const builders: Record<Method, () => Suggestion | undefined> = {
83
85
  Role: () => roleSuggestion(widget, variant),
84
86
  LabelText: () =>
package/src/traversal.ts CHANGED
@@ -4,14 +4,14 @@ import * as Gtk from "@gtkx/gi/gtk";
4
4
  * A scope that resolves to a single root widget: the widget itself, the widget a controller or
5
5
  * layout manager is attached to, or a list item's or header's child.
6
6
  */
7
- type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;
7
+ type QueryContainer = Gtk.Accessible | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;
8
8
  /**
9
9
  * A scope that queries and traversal can run against: a widget, an event
10
10
  * controller, a layout manager, a list item or header, an application, or the
11
11
  * sentinel representing all current toplevel windows.
12
12
  */
13
13
  type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;
14
- type WidgetClass<T extends Gtk.Widget> = abstract new (...args: never[]) => T;
14
+ type WidgetClass<T extends object> = abstract new (...args: never[]) => T;
15
15
 
16
16
  /** Container sentinel that widens a query to every toplevel window currently open. */
17
17
  const TOPLEVELS: unique symbol = Symbol("gtkx.toplevels");
@@ -60,7 +60,7 @@ const ancestors = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {
60
60
  }
61
61
  };
62
62
 
63
- const ancestorFor = <T extends Gtk.Widget>(widget: Gtk.Widget, type: WidgetClass<T>): T | null => {
63
+ const ancestorFor = <T extends object>(widget: Gtk.Widget, type: WidgetClass<T>): T | null => {
64
64
  for (const ancestor of ancestors(widget)) {
65
65
  if (ancestor instanceof type) {
66
66
  return ancestor;
@@ -98,7 +98,7 @@ const resolveRoot = (container: QueryContainer): Gtk.Widget | null => {
98
98
  return container.getChild();
99
99
  }
100
100
 
101
- return null;
101
+ throw new TypeError("Query container must resolve to a Gtk.Widget");
102
102
  };
103
103
 
104
104
  const roots = function* (container: Container): Generator<Gtk.Widget> {
package/src/types.ts CHANGED
@@ -15,9 +15,11 @@ type Matcher = string | number | RegExp | MatcherFunction;
15
15
  type NormalizerFn = (text: string) => string;
16
16
  /**
17
17
  * A widget class usable as a query's `as` constraint, such as `Gtk.Button`. Abstract classes and
18
- * generated GInterface pseudo-classes are accepted.
18
+ * generated GInterface pseudo-classes that extend `Gtk.Accessible` are accepted.
19
19
  */
20
- type WidgetType<T extends Gtk.Widget = Gtk.Widget> = abstract new (...args: never[]) => T;
20
+ type WidgetType<T extends Gtk.Accessible = Gtk.Accessible> = abstract new (
21
+ ...args: never[]
22
+ ) => T;
21
23
 
22
24
  /** Options controlling the default text normalizer. */
23
25
  type NormalizerOptions = {
@@ -40,7 +42,7 @@ type WaitForOptions = {
40
42
  };
41
43
 
42
44
  /** Options controlling text matching and, for asynchronous queries, polling behavior. */
43
- type MatcherOptions<T extends Gtk.Widget = Gtk.Widget> = {
45
+ type MatcherOptions<T extends Gtk.Accessible = Gtk.Accessible> = {
44
46
  /** When true (the default), require an exact match; when false, match case-insensitively as a substring. */
45
47
  exact?: boolean | undefined;
46
48
  /** Custom normalizer replacing the default; cannot be combined with `trim` or `collapseWhitespace`. */
@@ -74,7 +76,7 @@ type ByRoleValue = {
74
76
  };
75
77
 
76
78
  /** Options for role queries: an accessible name matcher plus accessible state and value constraints. */
77
- type ByRoleOptions<T extends Gtk.Widget = Gtk.Widget> = MatcherOptions<T> & {
79
+ type ByRoleOptions<T extends Gtk.Accessible = Gtk.Accessible> = MatcherOptions<T> & {
78
80
  /** Matcher for the widget's accessible name. */
79
81
  name?: Matcher | undefined;
80
82
  /** Required checked state; a mixed check button reads as neither, so it matches neither value. */
@@ -116,7 +118,7 @@ type BoundQuery<Q extends Query> = Q extends (container: Container, ...args: inf
116
118
  type BoundCustomQueries<Q extends QueryMap> = { [K in keyof Q]: BoundQuery<Q[K]> };
117
119
 
118
120
  /** What each variant of a query family yields for the widget type it matched. */
119
- type QueryFamilyReturns<T extends Gtk.Widget> = {
121
+ type QueryFamilyReturns<T extends Gtk.Accessible> = {
120
122
  /** The single match, or null when nothing matched; throws when more than one matched. */
121
123
  queryBy: T | null;
122
124
  /** Every match, empty when nothing matched. */
@@ -135,7 +137,7 @@ type QueryFamilyReturns<T extends Gtk.Widget> = {
135
137
  type QueryKind = "role" | "text" | "name" | "value";
136
138
 
137
139
  /** The arguments a query of the given kind takes after the family's leading ones. */
138
- type QueryArgs<Kind extends QueryKind, T extends Gtk.Widget> = Kind extends "role"
140
+ type QueryArgs<Kind extends QueryKind, T extends Gtk.Accessible> = Kind extends "role"
139
141
  ? [role: Gtk.AccessibleRole, options?: ByRoleOptions<T>]
140
142
  : Kind extends "name"
141
143
  ? [name: Matcher, options?: MatcherOptions<T>]
@@ -148,7 +150,9 @@ type QueryArgs<Kind extends QueryKind, T extends Gtk.Widget> = Kind extends "rol
148
150
  * ahead of the family's own arguments.
149
151
  */
150
152
  type QueryFamily<Suffix extends string, Kind extends QueryKind, Head extends unknown[]> = {
151
- [K in keyof QueryFamilyReturns<Gtk.Widget> as `${K & string}${Suffix}`]: <T extends Gtk.Widget = Gtk.Widget>(
153
+ [K in keyof QueryFamilyReturns<Gtk.Widget> as `${K & string}${Suffix}`]: <
154
+ T extends Gtk.Accessible = Gtk.Widget,
155
+ >(
152
156
  ...args: [...Head, ...QueryArgs<Kind, T>]
153
157
  ) => QueryFamilyReturns<T>[K];
154
158
  };
@@ -175,7 +179,7 @@ type RenderOptions<Q extends QueryMap = Record<never, never>> = {
175
179
  * out of reach of a pointer and a keyboard, so every `userEvent` helper aimed at it rejects
176
180
  * after `actionabilityTimeout`; `fireEvent` drives such a tree instead.
177
181
  */
178
- container?: Gtk.Widget | RootElement | undefined;
182
+ container?: Gtk.Accessible | RootElement | undefined;
179
183
  /** Root of the subtree that bound queries search. */
180
184
  baseElement?: Container | undefined;
181
185
  /** Component wrapped around the rendered element, such as a context provider. */
@@ -1,4 +1,5 @@
1
1
  import type * as Gtk from "@gtkx/gi/gtk";
2
+ import { requireWidget } from "../widget-target.js";
2
3
 
3
4
  /** An event controller class that can be constructed without arguments, such as `Gtk.GestureClick`. */
4
5
  type ControllerConstructor<T extends Gtk.EventController> = new () => T;
@@ -11,9 +12,10 @@ type ControllerConstructor<T extends Gtk.EventController> = new () => T;
11
12
  * @returns The matching controllers, or an empty array when the widget has none.
12
13
  */
13
14
  const queryAllControllers = <T extends Gtk.EventController>(
14
- widget: Gtk.Widget,
15
+ target: Gtk.Accessible,
15
16
  controllerType: ControllerConstructor<T>,
16
17
  ): T[] => {
18
+ const widget = requireWidget(target);
17
19
  const controllers = widget.observeControllers();
18
20
  const nItems = controllers.getNItems();
19
21
  const matches: T[] = [];
@@ -37,7 +39,7 @@ const queryAllControllers = <T extends Gtk.EventController>(
37
39
  * @returns The first matching controller, or null when the widget has none.
38
40
  */
39
41
  const queryController = <T extends Gtk.EventController>(
40
- widget: Gtk.Widget,
42
+ widget: Gtk.Accessible,
41
43
  controllerType: ControllerConstructor<T>,
42
44
  ): T | null => queryAllControllers(widget, controllerType)[0] ?? null;
43
45
 
@@ -49,7 +51,7 @@ const queryController = <T extends Gtk.EventController>(
49
51
  * @throws When the widget has no controller of that type.
50
52
  */
51
53
  const getAllControllers = <T extends Gtk.EventController>(
52
- widget: Gtk.Widget,
54
+ widget: Gtk.Accessible,
53
55
  controllerType: ControllerConstructor<T>,
54
56
  ): T[] => {
55
57
  const controllers = queryAllControllers(widget, controllerType);
@@ -69,7 +71,7 @@ const getAllControllers = <T extends Gtk.EventController>(
69
71
  * @throws When the widget has no controller of that type.
70
72
  */
71
73
  const getController = <T extends Gtk.EventController>(
72
- widget: Gtk.Widget,
74
+ widget: Gtk.Accessible,
73
75
  controllerType: ControllerConstructor<T>,
74
76
  ): T => {
75
77
  const [controller] = getAllControllers(widget, controllerType);
@@ -82,9 +84,10 @@ const getController = <T extends Gtk.EventController>(
82
84
  };
83
85
 
84
86
  const getOrCreateControllers = <T extends Gtk.EventController>(
85
- widget: Gtk.Widget,
87
+ target: Gtk.Accessible,
86
88
  controllerType: ControllerConstructor<T>,
87
89
  ): T[] => {
90
+ const widget = requireWidget(target);
88
91
  const existing = queryAllControllers(widget, controllerType);
89
92
 
90
93
  if (existing.length > 0) {
@@ -1,8 +1,19 @@
1
1
  import type * as Gtk from "@gtkx/gi/gtk";
2
2
  import { delay } from "../timers.js";
3
+ import { requireWidget } from "../widget-target.js";
3
4
  import { scroll, slide } from "./adjustment.js";
4
5
  import { click, dblClick, tripleClick } from "./click.js";
5
- import { drag, dragAndDrop, drop, hover, longPress, rotate, swipe, unhover, zoom } from "./gesture.js";
6
+ import {
7
+ drag,
8
+ dragAndDrop,
9
+ drop,
10
+ hover,
11
+ longPress,
12
+ rotate,
13
+ swipe,
14
+ unhover,
15
+ zoom,
16
+ } from "./gesture.js";
6
17
  import { keyboard, tab } from "./keyboard.js";
7
18
  import { pointer, type PointerInput } from "./pointer.js";
8
19
  import { deselectOptions, selectOptions } from "./selection.js";
@@ -20,55 +31,126 @@ type UserEvent = {
20
31
  /** Creates an instance with shared keyboard and pointer state. */
21
32
  setup: (options?: UserEventOptions) => UserEvent;
22
33
  /** Activates or presses and releases a widget. */
23
- click: typeof click;
34
+ click: (
35
+ widget: Gtk.Accessible,
36
+ ...args: Parameters<typeof click> extends [Gtk.Widget, ...infer Args] ? Args : never
37
+ ) => Promise<void>;
24
38
  /** Delivers a double-click gesture. */
25
- dblClick: typeof dblClick;
39
+ dblClick: (
40
+ widget: Gtk.Accessible,
41
+ ...args: Parameters<typeof dblClick> extends [Gtk.Widget, ...infer Args] ? Args : never
42
+ ) => Promise<void>;
26
43
  /** Delivers a triple-click gesture. */
27
- tripleClick: typeof tripleClick;
44
+ tripleClick: (
45
+ widget: Gtk.Accessible,
46
+ ...args: Parameters<typeof tripleClick> extends [Gtk.Widget, ...infer Args] ? Args : never
47
+ ) => Promise<void>;
28
48
  /** Moves focus forward or backward within the root. */
29
- tab: typeof tab;
49
+ tab: (
50
+ widget: Gtk.Accessible,
51
+ ...args: Parameters<typeof tab> extends [Gtk.Widget, ...infer Args] ? Args : never
52
+ ) => Promise<void>;
30
53
  /** Types into an editable widget. */
31
- type: typeof type;
54
+ type: (
55
+ widget: Gtk.Accessible,
56
+ ...args: Parameters<typeof type> extends [Gtk.Widget, ...infer Args] ? Args : never
57
+ ) => Promise<void>;
32
58
  /** Deletes all text from an editable widget. */
33
- clear: typeof clear;
59
+ clear: (
60
+ widget: Gtk.Accessible,
61
+ ...args: Parameters<typeof clear> extends [Gtk.Widget, ...infer Args] ? Args : never
62
+ ) => Promise<void>;
34
63
  /** Copies the current selection. */
35
- copy: typeof copy;
64
+ copy: (
65
+ widget: Gtk.Accessible,
66
+ ...args: Parameters<typeof copy> extends [Gtk.Widget, ...infer Args] ? Args : never
67
+ ) => Promise<void>;
36
68
  /** Cuts the current selection. */
37
- cut: typeof cut;
69
+ cut: (
70
+ widget: Gtk.Accessible,
71
+ ...args: Parameters<typeof cut> extends [Gtk.Widget, ...infer Args] ? Args : never
72
+ ) => Promise<void>;
38
73
  /** Pastes text or clipboard contents. */
39
- paste: typeof paste;
74
+ paste: (
75
+ widget: Gtk.Accessible,
76
+ ...args: Parameters<typeof paste> extends [Gtk.Widget, ...infer Args] ? Args : never
77
+ ) => Promise<void>;
40
78
  /** Selects positions without activating them. */
41
79
  selectOptions: typeof selectOptions;
42
80
  /** Unselects positions without activating them. */
43
81
  deselectOptions: typeof deselectOptions;
44
82
  /** Emits a motion `enter` on the widget, adding a motion controller when it has none. */
45
- hover: typeof hover;
83
+ hover: (
84
+ widget: Gtk.Accessible,
85
+ ...args: Parameters<typeof hover> extends [Gtk.Widget, ...infer Args] ? Args : never
86
+ ) => Promise<void>;
46
87
  /** Emits a motion `leave` on the widget, adding a motion controller when it has none. */
47
- unhover: typeof unhover;
88
+ unhover: (
89
+ widget: Gtk.Accessible,
90
+ ...args: Parameters<typeof unhover> extends [Gtk.Widget, ...infer Args] ? Args : never
91
+ ) => Promise<void>;
48
92
  /** Emits `angle-changed` on the widget's rotate gestures. */
49
- rotate: typeof rotate;
93
+ rotate: (
94
+ widget: Gtk.Accessible,
95
+ ...args: Parameters<typeof rotate> extends [Gtk.Widget, ...infer Args] ? Args : never
96
+ ) => Promise<void>;
50
97
  /** Emits `scale-changed` on the widget's zoom gestures. */
51
- zoom: typeof zoom;
98
+ zoom: (
99
+ widget: Gtk.Accessible,
100
+ ...args: Parameters<typeof zoom> extends [Gtk.Widget, ...infer Args] ? Args : never
101
+ ) => Promise<void>;
52
102
  /** Emits `swipe` with the given velocity on the widget's swipe gestures. */
53
- swipe: typeof swipe;
103
+ swipe: (
104
+ widget: Gtk.Accessible,
105
+ ...args: Parameters<typeof swipe> extends [Gtk.Widget, ...infer Args] ? Args : never
106
+ ) => Promise<void>;
54
107
  /** Emits `pressed` at the given point on the widget's long-press gestures. */
55
- longPress: typeof longPress;
108
+ longPress: (
109
+ widget: Gtk.Accessible,
110
+ ...args: Parameters<typeof longPress> extends [Gtk.Widget, ...infer Args] ? Args : never
111
+ ) => Promise<void>;
56
112
  /** Runs a begin, update, and end sequence on the widget's drag gestures, ending at the given offset. */
57
- drag: typeof drag;
113
+ drag: (
114
+ widget: Gtk.Accessible,
115
+ ...args: Parameters<typeof drag> extends [Gtk.Widget, ...infer Args] ? Args : never
116
+ ) => Promise<void>;
58
117
  /** Emits `drop` with the given content on the widget's drop targets. */
59
- drop: typeof drop;
118
+ drop: (
119
+ widget: Gtk.Accessible,
120
+ ...args: Parameters<typeof drop> extends [Gtk.Widget, ...infer Args] ? Args : never
121
+ ) => Promise<void>;
60
122
  /** Requires a drag source on the source widget, then emits `drop` with the content on the target's drop targets. */
61
- dragAndDrop: typeof dragAndDrop;
123
+ dragAndDrop: (
124
+ source: Gtk.Accessible,
125
+ target: Gtk.Accessible,
126
+ ...args: Parameters<typeof dragAndDrop> extends [Gtk.Widget, Gtk.Widget, ...infer Args] ? Args : never
127
+ ) => Promise<void>;
62
128
  /** Emits a jump `change-value` on a Gtk.Range so it moves to the given value. */
63
- slide: typeof slide;
129
+ slide: (
130
+ widget: Gtk.Accessible,
131
+ ...args: Parameters<typeof slide> extends [Gtk.Widget, ...infer Args] ? Args : never
132
+ ) => Promise<void>;
64
133
  /** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */
65
- scroll: typeof scroll;
134
+ scroll: (
135
+ widget: Gtk.Accessible,
136
+ ...args: Parameters<typeof scroll> extends [Gtk.Widget, ...infer Args] ? Args : never
137
+ ) => Promise<void>;
66
138
  /** Sends a key sequence through GTK's propagation chain. */
67
- keyboard: (widget: Gtk.Widget, input: string) => Promise<void>;
139
+ keyboard: (widget: Gtk.Accessible, input: string) => Promise<void>;
68
140
  /** Applies a pointer token while preserving held-button state. */
69
- pointer: (widget: Gtk.Widget, input: PointerInput) => Promise<void>;
141
+ pointer: (widget: Gtk.Accessible, input: PointerInput) => Promise<void>;
70
142
  };
71
143
 
144
+ const onWidget =
145
+ <Args extends unknown[]>(helper: (widget: Gtk.Widget, ...args: Args) => Promise<void>) =>
146
+ (target: Gtk.Accessible, ...args: Args): Promise<void> =>
147
+ helper(requireWidget(target), ...args);
148
+
149
+ const onWidgetPair =
150
+ <Args extends unknown[]>(helper: (source: Gtk.Widget, target: Gtk.Widget, ...args: Args) => Promise<void>) =>
151
+ (source: Gtk.Accessible, target: Gtk.Accessible, ...args: Args): Promise<void> =>
152
+ helper(requireWidget(source), requireWidget(target), ...args);
153
+
72
154
  /** User interactions that dispatch GTK events and gestures. */
73
155
  const userEvent: UserEvent = {
74
156
  ...createInstance(createInitialState(), {}),
@@ -93,30 +175,34 @@ function createInstance(state: UserEventState, options: UserEventOptions): UserE
93
175
 
94
176
  return {
95
177
  setup: (overrides?: UserEventOptions): UserEvent => createInstance(state, { ...options, ...overrides }),
96
- click: after(click),
97
- dblClick: after(dblClick),
98
- tripleClick: after(tripleClick),
99
- tab: after(tab),
100
- type: after(type),
101
- clear: after(clear),
102
- copy: after(copy),
103
- cut: after(cut),
104
- paste: after(paste),
105
- selectOptions: after(selectOptions),
106
- deselectOptions: after(deselectOptions),
107
- hover: after(hover),
108
- unhover: after(unhover),
109
- rotate: after(rotate),
110
- zoom: after(zoom),
111
- swipe: after(swipe),
112
- longPress: after(longPress),
113
- drag: after(drag),
114
- drop: after(drop),
115
- dragAndDrop: after(dragAndDrop),
116
- slide: after(slide),
117
- scroll: after(scroll),
118
- keyboard: after((widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input)),
119
- pointer: after((widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input)),
178
+ click: after(onWidget(click)),
179
+ dblClick: after(onWidget(dblClick)),
180
+ tripleClick: after(onWidget(tripleClick)),
181
+ tab: after(onWidget(tab)),
182
+ type: after(onWidget(type)),
183
+ clear: after(onWidget(clear)),
184
+ copy: after(onWidget(copy)),
185
+ cut: after(onWidget(cut)),
186
+ paste: after(onWidget(paste)),
187
+ selectOptions: after(onWidget(selectOptions)),
188
+ deselectOptions: after(onWidget(deselectOptions)),
189
+ hover: after(onWidget(hover)),
190
+ unhover: after(onWidget(unhover)),
191
+ rotate: after(onWidget(rotate)),
192
+ zoom: after(onWidget(zoom)),
193
+ swipe: after(onWidget(swipe)),
194
+ longPress: after(onWidget(longPress)),
195
+ drag: after(onWidget(drag)),
196
+ drop: after(onWidget(drop)),
197
+ dragAndDrop: after(onWidgetPair(dragAndDrop)),
198
+ slide: after(onWidget(slide)),
199
+ scroll: after(onWidget(scroll)),
200
+ keyboard: after(
201
+ onWidget((widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input)),
202
+ ),
203
+ pointer: after(
204
+ onWidget((widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input)),
205
+ ),
120
206
  };
121
207
  }
122
208
 
@@ -153,9 +153,17 @@ const runSelectionEvent = (
153
153
  byRole(widget, valueArray);
154
154
  });
155
155
 
156
+ const requireWidget = (target: Gtk.Accessible): Gtk.Widget => {
157
+ if (!(target instanceof Gtk.Widget)) {
158
+ throw new TypeError("Cannot select options on a non-widget accessible");
159
+ }
160
+
161
+ return target;
162
+ };
163
+
156
164
  /** Selects positions without activating them. */
157
- const selectOptions = (widget: Gtk.Widget, values: number | number[]): Promise<void> =>
158
- runSelectionEvent(widget, values, selectInListView, selectByRole);
165
+ const selectOptions = (widget: Gtk.Accessible, values: number | number[]): Promise<void> =>
166
+ runSelectionEvent(requireWidget(widget), values, selectInListView, selectByRole);
159
167
 
160
168
  const deselectInListView = (widget: Gtk.ListView | Gtk.GridView | Gtk.ColumnView, valueArray: number[]): void => {
161
169
  const selectionModel = requireSelectionModel(widget, "deselect");
@@ -176,7 +184,7 @@ const deselectByRole = (widget: Gtk.Widget, valueArray: number[]): void => {
176
184
  };
177
185
 
178
186
  /** Unselects positions without activating them. */
179
- const deselectOptions = (widget: Gtk.Widget, values: number | number[]): Promise<void> =>
180
- runSelectionEvent(widget, values, deselectInListView, deselectByRole);
187
+ const deselectOptions = (widget: Gtk.Accessible, values: number | number[]): Promise<void> =>
188
+ runSelectionEvent(requireWidget(widget), values, deselectInListView, deselectByRole);
181
189
 
182
190
  export { selectOptions, deselectOptions };
package/src/wait-for.ts CHANGED
@@ -4,12 +4,13 @@ import { runWithActEnvironment } from "./act.js";
4
4
  import { getConfig } from "./config.js";
5
5
  import { timeoutError } from "./errors.js";
6
6
  import { advanceFakeClock, delay, now } from "./timers.js";
7
+ import { requireWidget } from "./widget-target.js";
7
8
 
8
9
  type PollResult<T> = { status: "resolved"; value: T } | { status: "timedout"; lastError: Error | null };
9
10
  /** Widgets watched for removal; null counts as already removed. */
10
- type RemovalTarget = Gtk.Widget | Gtk.Widget[] | null;
11
+ type RemovalTarget = Gtk.Accessible | Gtk.Accessible[] | null;
11
12
  /** The widgets to watch for removal, or a function re-read on every poll to locate them. */
12
- type ElementOrCallback = Gtk.Widget | Gtk.Widget[] | (() => RemovalTarget);
13
+ type ElementOrCallback = Gtk.Accessible | Gtk.Accessible[] | (() => RemovalTarget);
13
14
 
14
15
  const DEFAULT_INTERVAL = 50;
15
16
  const ELEMENT_NOT_REMOVED = new Error("Element not yet removed");
@@ -102,10 +103,8 @@ const waitFor = <T>(callback: () => T | Promise<T>, options?: WaitForOptions): P
102
103
  const getTarget = (elementOrCallback: ElementOrCallback): RemovalTarget =>
103
104
  typeof elementOrCallback === "function" ? elementOrCallback() : elementOrCallback;
104
105
 
105
- const isWidgetRemoved = (widget: Gtk.Widget | null): boolean => {
106
- if (widget === null) {
107
- return true;
108
- }
106
+ const isWidgetRemoved = (target: Gtk.Accessible): boolean => {
107
+ const widget = requireWidget(target);
109
108
 
110
109
  try {
111
110
  return widget.getRoot() === null;
@@ -115,6 +114,10 @@ const isWidgetRemoved = (widget: Gtk.Widget | null): boolean => {
115
114
  };
116
115
 
117
116
  const isTargetRemoved = (target: RemovalTarget): boolean => {
117
+ if (target === null) {
118
+ return true;
119
+ }
120
+
118
121
  if (Array.isArray(target)) {
119
122
  return target.every((widget) => isWidgetRemoved(widget));
120
123
  }
@@ -13,6 +13,7 @@ import { EDITABLE_ROLES, isEditable, readEditableText } from "./editable.js";
13
13
  import { isNameFromAuthor, isNameProhibited } from "./role-naming.js";
14
14
  import { children, descendants, relationCandidates } from "./traversal.js";
15
15
  import { callBooleanGetter, callStringGetter, getCallableMethod } from "./widget-getters.js";
16
+ import { requireWidget } from "./widget-target.js";
16
17
 
17
18
  type WidgetValueField = "now" | "min" | "max";
18
19
  type CheckedState = "checked" | "unchecked" | "mixed";
@@ -64,12 +65,12 @@ const getLabelText = (widget: Gtk.Widget): string | null => {
64
65
  };
65
66
 
66
67
  const stripMnemonic = (text: string): string => text.replaceAll(/_(.)/g, "$1");
67
- const isUnderlineUsed = (widget: Gtk.Widget): boolean => callBooleanGetter(widget, "getUseUnderline") ?? false;
68
+ const isUnderlineUsed = (widget: object): boolean => callBooleanGetter(widget, "getUseUnderline") ?? false;
68
69
 
69
- const readNamingText = (widget: Gtk.Widget, getter: string, value: string): string =>
70
+ const readNamingText = (widget: object, getter: string, value: string): string =>
70
71
  getter !== EDITABLE_TEXT_GETTER && isUnderlineUsed(widget) ? stripMnemonic(value) : value;
71
72
 
72
- const readFirstText = (widget: Gtk.Widget, getters: string[]): string | null => {
73
+ const readFirstText = (widget: object, getters: string[]): string | null => {
73
74
  for (const getter of getters) {
74
75
  const value = callStringGetter(widget, getter);
75
76
 
@@ -87,7 +88,7 @@ const readFirstText = (widget: Gtk.Widget, getters: string[]): string | null =>
87
88
  *
88
89
  * @param widget The widget to read text from.
89
90
  */
90
- const getWidgetText = (widget: Gtk.Widget): string | null => readFirstText(widget, DEFAULT_TEXT_GETTERS);
91
+ const getWidgetText = (widget: Gtk.Accessible): string | null => readFirstText(widget, DEFAULT_TEXT_GETTERS);
91
92
 
92
93
  const namingLabelText = (widget: Gtk.Widget): string | null => {
93
94
  const text = getLabelText(widget);
@@ -446,7 +447,8 @@ const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
446
447
  *
447
448
  * @param widget The widget to test.
448
449
  */
449
- const isInaccessible = (widget: Gtk.Widget): boolean => {
450
+ const isInaccessible = (target: Gtk.Accessible): boolean => {
451
+ const widget = requireWidget(target);
450
452
  let current: Gtk.Widget | null = widget;
451
453
 
452
454
  while (current) {
@@ -1,28 +1,26 @@
1
- import type * as GObject from "@gtkx/gi/gobject";
2
- import type * as Gtk from "@gtkx/gi/gtk";
3
1
  import { getClassType, getInstanceType, TYPE_INVALID, typeName } from "@gtkx/runtime";
4
2
  import { resolveWrapperClass } from "@gtkx/runtime/internal";
5
3
  import { type AnyClass, walkClassChain } from "@gtkx/utils";
6
4
 
7
5
  const UNNAMED_TYPE_TAG = "Object";
8
6
 
9
- const getWidgetMethod = (widget: Gtk.Widget, name: string): unknown => Reflect.get(widget, name);
10
- const getWidgetTypeName = (widget: Gtk.Widget): string | null => typeName(getInstanceType(widget));
7
+ const getWidgetMethod = (widget: object, name: string): unknown => Reflect.get(widget, name);
8
+ const getWidgetTypeName = (widget: object): string | null => typeName(getInstanceType(widget));
11
9
  const isClass = (value: unknown): value is AnyClass => typeof value === "function";
12
10
 
13
11
  const nonEmpty = (value: unknown): string | undefined =>
14
12
  typeof value === "string" && value.length > 0 ? value : undefined;
15
13
 
16
- const getObjectClass = (object: GObject.Object): AnyClass | null => {
14
+ const getObjectClass = (object: object): AnyClass | null => {
17
15
  const cls: unknown = object.constructor;
18
16
 
19
17
  return isClass(cls) ? cls : null;
20
18
  };
21
19
 
22
- const getNearestClassName = (object: GObject.Object): string | undefined =>
20
+ const getNearestClassName = (object: object): string | undefined =>
23
21
  walkClassChain(getObjectClass(object), (ancestor) => nonEmpty(ancestor.name));
24
22
 
25
- const getUntypedClassName = (object: GObject.Object): string | undefined => {
23
+ const getUntypedClassName = (object: object): string | undefined => {
26
24
  const cls = getObjectClass(object);
27
25
 
28
26
  return cls !== null && getClassType(cls) === TYPE_INVALID ? nonEmpty(cls.name) : undefined;
@@ -34,7 +32,7 @@ const getExactWrapperName = (type: bigint): string | undefined => {
34
32
  return wrapper !== null && getClassType(wrapper) === type ? nonEmpty(wrapper.name) : undefined;
35
33
  };
36
34
 
37
- const getTypeTag = (object: GObject.Object): string => {
35
+ const getTypeTag = (object: object): string => {
38
36
  const type = getInstanceType(object);
39
37
 
40
38
  return (
@@ -46,15 +44,15 @@ const getTypeTag = (object: GObject.Object): string => {
46
44
  );
47
45
  };
48
46
 
49
- const isDefaultWidgetName = (widget: Gtk.Widget, name: string): boolean =>
47
+ const isDefaultWidgetName = (widget: object, name: string): boolean =>
50
48
  name.length === 0 || name === getWidgetTypeName(widget);
51
49
 
52
- const hasWidgetMethod = (widget: Gtk.Widget, name: string): boolean =>
50
+ const hasWidgetMethod = (widget: object, name: string): boolean =>
53
51
  typeof getWidgetMethod(widget, name) === "function";
54
52
 
55
53
  /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */
56
54
  const getCallableMethod = <Args extends unknown[], Result>(
57
- widget: Gtk.Widget,
55
+ widget: object,
58
56
  name: string,
59
57
  ): ((...args: Args) => Result) | null => {
60
58
  const fn = getWidgetMethod(widget, name);
@@ -62,13 +60,13 @@ const getCallableMethod = <Args extends unknown[], Result>(
62
60
  return typeof fn === "function" ? (fn as (...args: Args) => Result).bind(widget) : null;
63
61
  };
64
62
 
65
- const callBooleanGetter = (widget: Gtk.Widget, method: string): boolean | null => {
63
+ const callBooleanGetter = (widget: object, method: string): boolean | null => {
66
64
  const value = getCallableMethod<[], unknown>(widget, method)?.();
67
65
 
68
66
  return typeof value === "boolean" ? value : null;
69
67
  };
70
68
 
71
- const callStringGetter = (widget: Gtk.Widget, method: string): string | null => {
69
+ const callStringGetter = (widget: object, method: string): string | null => {
72
70
  const value = getCallableMethod<[], unknown>(widget, method)?.();
73
71
 
74
72
  return typeof value === "string" ? value : null;
@@ -0,0 +1,11 @@
1
+ import * as Gtk from "@gtkx/gi/gtk";
2
+
3
+ const requireWidget = (target: object): Gtk.Widget => {
4
+ if (!(target instanceof Gtk.Widget)) {
5
+ throw new TypeError("Expected a Gtk.Widget");
6
+ }
7
+
8
+ return target;
9
+ };
10
+
11
+ export { requireWidget };