@gtkx/testing 2.0.0-beta.3 → 2.0.0-beta.5
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.
- package/dist/build-queries.d.ts +9 -9
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +2 -1
- package/dist/build-queries.js.map +1 -1
- package/dist/fire-event.d.ts +3 -3
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +14 -4
- package/dist/fire-event.js.map +1 -1
- package/dist/matchers.d.ts +6 -6
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +3 -2
- package/dist/matchers.js.map +1 -1
- package/dist/queries.d.ts +7 -7
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +16 -9
- package/dist/queries.js.map +1 -1
- package/dist/query-helpers.d.ts +2 -2
- package/dist/query-helpers.d.ts.map +1 -1
- package/dist/query-helpers.js +2 -1
- package/dist/query-helpers.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +4 -0
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +1 -1
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +2 -1
- package/dist/role-helpers.js.map +1 -1
- package/dist/screenshot.d.ts +1 -1
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +3 -1
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +1 -1
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js +3 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +3 -3
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +1 -1
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +8 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/controller.d.ts +5 -5
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +5 -2
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/index.d.ts +22 -22
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +28 -25
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/selection.d.ts +2 -2
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +8 -2
- package/dist/user-event/selection.js.map +1 -1
- package/dist/wait-for.d.ts +2 -2
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js +6 -4
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +2 -2
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +3 -1
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/widget-getters.d.ts +7 -9
- package/dist/widget-getters.d.ts.map +1 -1
- package/dist/widget-getters.js.map +1 -1
- package/dist/widget-target.d.ts +4 -0
- package/dist/widget-target.d.ts.map +1 -0
- package/dist/widget-target.js +9 -0
- package/dist/widget-target.js.map +1 -0
- package/package.json +5 -5
- package/src/build-queries.ts +60 -30
- package/src/fire-event.ts +32 -8
- package/src/matchers.ts +13 -12
- package/src/queries.ts +61 -29
- package/src/query-helpers.ts +9 -7
- package/src/render.tsx +5 -0
- package/src/role-helpers.ts +3 -1
- package/src/screenshot.ts +3 -1
- package/src/suggestions.ts +3 -1
- package/src/traversal.ts +4 -4
- package/src/types.ts +12 -8
- package/src/user-event/controller.ts +8 -5
- package/src/user-event/index.ts +133 -47
- package/src/user-event/selection.ts +12 -4
- package/src/wait-for.ts +9 -6
- package/src/widget-accessible-properties.ts +7 -5
- package/src/widget-getters.ts +11 -13
- 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 (
|
|
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) {
|
package/src/suggestions.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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}`]: <
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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) {
|
package/src/user-event/index.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
139
|
+
keyboard: (widget: Gtk.Accessible, input: string) => Promise<void>;
|
|
68
140
|
/** Applies a pointer token while preserving held-button state. */
|
|
69
|
-
pointer: (widget: Gtk.
|
|
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(
|
|
119
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 = (
|
|
106
|
-
|
|
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:
|
|
68
|
+
const isUnderlineUsed = (widget: object): boolean => callBooleanGetter(widget, "getUseUnderline") ?? false;
|
|
68
69
|
|
|
69
|
-
const readNamingText = (widget:
|
|
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:
|
|
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.
|
|
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 = (
|
|
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) {
|
package/src/widget-getters.ts
CHANGED
|
@@ -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:
|
|
10
|
-
const getWidgetTypeName = (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:
|
|
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:
|
|
20
|
+
const getNearestClassName = (object: object): string | undefined =>
|
|
23
21
|
walkClassChain(getObjectClass(object), (ancestor) => nonEmpty(ancestor.name));
|
|
24
22
|
|
|
25
|
-
const getUntypedClassName = (object:
|
|
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:
|
|
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:
|
|
47
|
+
const isDefaultWidgetName = (widget: object, name: string): boolean =>
|
|
50
48
|
name.length === 0 || name === getWidgetTypeName(widget);
|
|
51
49
|
|
|
52
|
-
const hasWidgetMethod = (widget:
|
|
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:
|
|
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:
|
|
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:
|
|
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;
|