@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/build-queries.ts
CHANGED
|
@@ -5,18 +5,28 @@ import { getConfig } from "./config.js";
|
|
|
5
5
|
import { runWithExpensiveErrorDiagnosticsDisabled, suggestionError } from "./errors.js";
|
|
6
6
|
import { getSuggestedQuery, type Variant } from "./suggestions.js";
|
|
7
7
|
import { waitFor } from "./wait-for.js";
|
|
8
|
+
import { requireWidget } from "./widget-target.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* The `queryAllBy` function a query family is built from, taking the scope to search followed by the
|
|
11
12
|
* family's own matcher arguments. Its name determines the family name, so `queryAllByTestId` builds
|
|
12
13
|
* a `TestId` family.
|
|
13
14
|
*/
|
|
14
|
-
type QueryAllBy<
|
|
15
|
+
type QueryAllBy<
|
|
16
|
+
Args extends unknown[],
|
|
17
|
+
Element extends Gtk.Accessible = Gtk.Accessible,
|
|
18
|
+
> = (
|
|
19
|
+
container: Container,
|
|
20
|
+
...args: Args
|
|
21
|
+
) => Element[];
|
|
15
22
|
|
|
16
23
|
/** Builds the error thrown when a single-match query finds more than one widget. */
|
|
17
|
-
type MultipleErrorBuilder<
|
|
24
|
+
type MultipleErrorBuilder<
|
|
25
|
+
Args extends unknown[],
|
|
26
|
+
Element extends Gtk.Accessible = Gtk.Accessible,
|
|
27
|
+
> = (
|
|
18
28
|
container: Container,
|
|
19
|
-
matches:
|
|
29
|
+
matches: Element[],
|
|
20
30
|
...args: Args
|
|
21
31
|
) => Error;
|
|
22
32
|
|
|
@@ -24,16 +34,22 @@ type MultipleErrorBuilder<Args extends unknown[]> = (
|
|
|
24
34
|
type MissingErrorBuilder<Args extends unknown[]> = (container: Container, ...args: Args) => Error;
|
|
25
35
|
|
|
26
36
|
/** The variants derived from one `queryAllBy` function, in the order DOM Testing Library returns them. */
|
|
27
|
-
type BuiltQueries<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
type BuiltQueries<
|
|
38
|
+
Args extends unknown[],
|
|
39
|
+
Element extends Gtk.Accessible = Gtk.Accessible,
|
|
40
|
+
> = [
|
|
41
|
+
queryBy: (container: Container, ...args: Args) => Element | null,
|
|
42
|
+
getAllBy: QueryAllBy<Args, Element>,
|
|
43
|
+
getBy: (container: Container, ...args: Args) => Element,
|
|
44
|
+
findAllBy: (container: Container, ...args: Args) => Promise<Element[]>,
|
|
45
|
+
findBy: (container: Container, ...args: Args) => Promise<Element>,
|
|
33
46
|
];
|
|
34
47
|
|
|
35
48
|
type FindQuery<Args extends unknown[]> = (container: Container, ...args: Args) => unknown;
|
|
36
|
-
type SingleQuery<Args extends unknown[]
|
|
49
|
+
type SingleQuery<Args extends unknown[], Element extends Gtk.Accessible> = (
|
|
50
|
+
container: Container,
|
|
51
|
+
...args: Args
|
|
52
|
+
) => Element | null;
|
|
37
53
|
|
|
38
54
|
const getTrailingOptions = (args: unknown[]): MatcherOptions | undefined => {
|
|
39
55
|
const last = args.at(-1);
|
|
@@ -50,7 +66,7 @@ const getQueryFamily = (queryAllBy: { name: string }): string => queryAllBy.name
|
|
|
50
66
|
|
|
51
67
|
const maybeThrowSuggestion = (options: {
|
|
52
68
|
container: Container;
|
|
53
|
-
match:
|
|
69
|
+
match: object;
|
|
54
70
|
queryName: string;
|
|
55
71
|
variant: Variant;
|
|
56
72
|
shouldSuggest: boolean | undefined;
|
|
@@ -61,7 +77,7 @@ const maybeThrowSuggestion = (options: {
|
|
|
61
77
|
return;
|
|
62
78
|
}
|
|
63
79
|
|
|
64
|
-
const suggestion = getSuggestedQuery(match, variant);
|
|
80
|
+
const suggestion = getSuggestedQuery(requireWidget(match), variant);
|
|
65
81
|
|
|
66
82
|
if (suggestion && suggestion.queryName !== queryName) {
|
|
67
83
|
throw suggestionError(suggestion.toString(), container);
|
|
@@ -101,10 +117,10 @@ const findOptions = <Args extends unknown[]>(
|
|
|
101
117
|
};
|
|
102
118
|
|
|
103
119
|
const singleFrom =
|
|
104
|
-
<Args extends unknown[]>(
|
|
105
|
-
allQuery: QueryAllBy<Args>,
|
|
106
|
-
getMultipleError: MultipleErrorBuilder<Args>,
|
|
107
|
-
): SingleQuery<Args> =>
|
|
120
|
+
<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
121
|
+
allQuery: QueryAllBy<Args, Element>,
|
|
122
|
+
getMultipleError: MultipleErrorBuilder<Args, Element>,
|
|
123
|
+
): SingleQuery<Args, Element> =>
|
|
108
124
|
(container, ...args) => {
|
|
109
125
|
const matches = allQuery(container, ...args);
|
|
110
126
|
|
|
@@ -116,10 +132,10 @@ const singleFrom =
|
|
|
116
132
|
};
|
|
117
133
|
|
|
118
134
|
const allOrThrow =
|
|
119
|
-
<Args extends unknown[]>(
|
|
120
|
-
allQuery: QueryAllBy<Args>,
|
|
135
|
+
<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
136
|
+
allQuery: QueryAllBy<Args, Element>,
|
|
121
137
|
getMissingError: MissingErrorBuilder<Args>,
|
|
122
|
-
): QueryAllBy<Args> =>
|
|
138
|
+
): QueryAllBy<Args, Element> =>
|
|
123
139
|
(container, ...args) => {
|
|
124
140
|
const matches = allQuery(container, ...args);
|
|
125
141
|
|
|
@@ -131,7 +147,11 @@ const allOrThrow =
|
|
|
131
147
|
};
|
|
132
148
|
|
|
133
149
|
const wrapSingleWithSuggestion =
|
|
134
|
-
<Args extends unknown[]
|
|
150
|
+
<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
151
|
+
query: SingleQuery<Args, Element>,
|
|
152
|
+
queryName: string,
|
|
153
|
+
variant: Variant,
|
|
154
|
+
): SingleQuery<Args, Element> =>
|
|
135
155
|
(container, ...args) => {
|
|
136
156
|
const match = query(container, ...args);
|
|
137
157
|
|
|
@@ -149,7 +169,11 @@ const wrapSingleWithSuggestion =
|
|
|
149
169
|
};
|
|
150
170
|
|
|
151
171
|
const wrapAllWithSuggestion =
|
|
152
|
-
<Args extends unknown[]
|
|
172
|
+
<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
173
|
+
query: QueryAllBy<Args, Element>,
|
|
174
|
+
queryName: string,
|
|
175
|
+
variant: Variant,
|
|
176
|
+
): QueryAllBy<Args, Element> =>
|
|
153
177
|
(container, ...args) => {
|
|
154
178
|
const matches = query(container, ...args);
|
|
155
179
|
const [first] = matches;
|
|
@@ -168,8 +192,11 @@ const wrapAllWithSuggestion =
|
|
|
168
192
|
};
|
|
169
193
|
|
|
170
194
|
const requireSingle =
|
|
171
|
-
<Args extends unknown[]
|
|
172
|
-
|
|
195
|
+
<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
196
|
+
query: SingleQuery<Args, Element>,
|
|
197
|
+
getMissingError: MissingErrorBuilder<Args>,
|
|
198
|
+
) =>
|
|
199
|
+
(container: Container, ...args: Args): Element => {
|
|
173
200
|
const match = query(container, ...args);
|
|
174
201
|
|
|
175
202
|
if (match === null) {
|
|
@@ -189,11 +216,14 @@ const requireSingle =
|
|
|
189
216
|
* @param getMissingError Builds the error thrown when a required match is missing.
|
|
190
217
|
* @returns The variants, in the order `[queryBy, getAllBy, getBy, findAllBy, findBy]`.
|
|
191
218
|
*/
|
|
192
|
-
const buildQueries = <
|
|
193
|
-
|
|
194
|
-
|
|
219
|
+
const buildQueries = <
|
|
220
|
+
Args extends unknown[],
|
|
221
|
+
Element extends Gtk.Accessible = Gtk.Accessible,
|
|
222
|
+
>(
|
|
223
|
+
queryAllBy: QueryAllBy<Args, Element>,
|
|
224
|
+
getMultipleError: MultipleErrorBuilder<Args, Element>,
|
|
195
225
|
getMissingError: MissingErrorBuilder<Args>,
|
|
196
|
-
): BuiltQueries<Args> => {
|
|
226
|
+
): BuiltQueries<Args, Element> => {
|
|
197
227
|
const queryName = getQueryFamily(queryAllBy);
|
|
198
228
|
const getAllBy = allOrThrow(queryAllBy, getMissingError);
|
|
199
229
|
const getBy = requireSingle(singleFrom(getAllBy, getMultipleError), getMissingError);
|
|
@@ -206,7 +236,7 @@ const buildQueries = <Args extends unknown[]>(
|
|
|
206
236
|
|
|
207
237
|
const getAllByWithSuggestion = wrapAllWithSuggestion(getAllBy, queryName, "getAll");
|
|
208
238
|
|
|
209
|
-
const getByWithSuggestion: (container: Container, ...args: Args) =>
|
|
239
|
+
const getByWithSuggestion: (container: Container, ...args: Args) => Element = (container, ...args) => {
|
|
210
240
|
const match = getBy(container, ...args);
|
|
211
241
|
|
|
212
242
|
maybeThrowSuggestion({
|
|
@@ -220,13 +250,13 @@ const buildQueries = <Args extends unknown[]>(
|
|
|
220
250
|
return match;
|
|
221
251
|
};
|
|
222
252
|
|
|
223
|
-
const findAllBy = (container: Container, ...args: Args): Promise<
|
|
253
|
+
const findAllBy = (container: Container, ...args: Args): Promise<Element[]> =>
|
|
224
254
|
waitFor(
|
|
225
255
|
() => runWithExpensiveErrorDiagnosticsDisabled(() => getAllByWithSuggestion(container, ...args)),
|
|
226
256
|
findOptions(getAllByWithSuggestion, container, args),
|
|
227
257
|
);
|
|
228
258
|
|
|
229
|
-
const findBy = (container: Container, ...args: Args): Promise<
|
|
259
|
+
const findBy = (container: Container, ...args: Args): Promise<Element> =>
|
|
230
260
|
waitFor(
|
|
231
261
|
() => runWithExpensiveErrorDiagnosticsDisabled(() => getByWithSuggestion(container, ...args)),
|
|
232
262
|
findOptions(getByWithSuggestion, container, args),
|
package/src/fire-event.ts
CHANGED
|
@@ -7,13 +7,21 @@ import { runInAct } from "./act.js";
|
|
|
7
7
|
*/
|
|
8
8
|
type WidgetEvent = {
|
|
9
9
|
/** The GObject instance the signal is emitted on. */
|
|
10
|
-
target: GObject.Object
|
|
10
|
+
target: Pick<GObject.Object, "__type__">;
|
|
11
11
|
/** Name of the signal to emit. */
|
|
12
12
|
signalName: string;
|
|
13
13
|
/** Arguments passed to the signal handlers. */
|
|
14
14
|
args: unknown[];
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const requireObject = (target: object): GObject.Object => {
|
|
18
|
+
if (!(target instanceof GObject.Object)) {
|
|
19
|
+
throw new TypeError("Expected a GObject instance");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return target;
|
|
23
|
+
};
|
|
24
|
+
|
|
17
25
|
/**
|
|
18
26
|
* Records a signal emission without delivering it, so it can be inspected or held and passed to
|
|
19
27
|
* {@link fireEvent} later.
|
|
@@ -23,18 +31,28 @@ type WidgetEvent = {
|
|
|
23
31
|
* @param args Arguments to pass to the signal handlers.
|
|
24
32
|
* @returns The recorded emission.
|
|
25
33
|
*/
|
|
26
|
-
const createEvent = (
|
|
27
|
-
target,
|
|
34
|
+
const createEvent = (
|
|
35
|
+
target: Pick<GObject.Object, "__type__">,
|
|
36
|
+
signalName: string,
|
|
37
|
+
...args: unknown[]
|
|
38
|
+
): WidgetEvent => ({
|
|
39
|
+
target: requireObject(target),
|
|
28
40
|
signalName,
|
|
29
41
|
args,
|
|
30
42
|
});
|
|
31
43
|
|
|
32
44
|
const dispatchEvent = async (event: WidgetEvent): Promise<void> => {
|
|
45
|
+
const target = requireObject(event.target);
|
|
46
|
+
|
|
33
47
|
await runInAct(() => {
|
|
34
|
-
Reflect.apply(GObject.signalEmit, undefined, [
|
|
48
|
+
Reflect.apply(GObject.signalEmit, undefined, [target, event.signalName, ...event.args]);
|
|
35
49
|
});
|
|
36
50
|
};
|
|
37
51
|
|
|
52
|
+
const isWidgetEvent = (value: object): value is WidgetEvent =>
|
|
53
|
+
"target" in value && "signalName" in value && "args" in value &&
|
|
54
|
+
typeof value.signalName === "string" && Array.isArray(value.args);
|
|
55
|
+
|
|
38
56
|
/**
|
|
39
57
|
* Emits a recorded signal emission inside React's act environment, so any resulting state updates are
|
|
40
58
|
* flushed before the promise resolves.
|
|
@@ -50,14 +68,18 @@ function fireEvent(event: WidgetEvent): Promise<void>;
|
|
|
50
68
|
* @param signalName Name of the signal to emit.
|
|
51
69
|
* @param args Arguments passed to the signal handlers.
|
|
52
70
|
*/
|
|
53
|
-
function fireEvent(
|
|
71
|
+
function fireEvent(
|
|
72
|
+
target: Pick<GObject.Object, "__type__">,
|
|
73
|
+
signalName: string,
|
|
74
|
+
...args: unknown[]
|
|
75
|
+
): Promise<void>;
|
|
54
76
|
|
|
55
77
|
function fireEvent(
|
|
56
|
-
eventOrTarget: WidgetEvent | GObject.Object,
|
|
78
|
+
eventOrTarget: WidgetEvent | Pick<GObject.Object, "__type__">,
|
|
57
79
|
signalName?: string,
|
|
58
80
|
...args: unknown[]
|
|
59
81
|
): Promise<void> {
|
|
60
|
-
if (
|
|
82
|
+
if (signalName === undefined && isWidgetEvent(eventOrTarget)) {
|
|
61
83
|
return dispatchEvent(eventOrTarget);
|
|
62
84
|
}
|
|
63
85
|
|
|
@@ -65,7 +87,9 @@ function fireEvent(
|
|
|
65
87
|
throw new Error("Unable to fire an event: pass a signal name, or an event built by createEvent.");
|
|
66
88
|
}
|
|
67
89
|
|
|
68
|
-
|
|
90
|
+
const target = requireObject(eventOrTarget);
|
|
91
|
+
|
|
92
|
+
return dispatchEvent({ target, signalName, args });
|
|
69
93
|
}
|
|
70
94
|
|
|
71
95
|
export { createEvent, fireEvent, type WidgetEvent };
|
package/src/matchers.ts
CHANGED
|
@@ -77,7 +77,7 @@ type TextQueryArgs = [text: Matcher, options?: MatcherOptions];
|
|
|
77
77
|
type ContainmentMatcher<Args extends unknown[]> = (received: unknown, ...args: Args) => MatcherResult;
|
|
78
78
|
type TextMatcherContext = { matcherName: string; widget: Gtk.Widget; actual: string | null };
|
|
79
79
|
type ClassArguments = { expected: ClassExpectation[]; isExact: boolean };
|
|
80
|
-
type QueryAll<Args extends unknown[]> = (container: Container, ...args: Args) =>
|
|
80
|
+
type QueryAll<Args extends unknown[]> = (container: Container, ...args: Args) => object[];
|
|
81
81
|
|
|
82
82
|
type BooleanAccessibleState =
|
|
83
83
|
Gtk.AccessibleState.BUSY |
|
|
@@ -195,11 +195,11 @@ type MatcherImplementations = {
|
|
|
195
195
|
/** Asserts the widget's accessible role. */
|
|
196
196
|
toHaveRole: (received: unknown, expected: Gtk.AccessibleRole) => MatcherResult;
|
|
197
197
|
/** Asserts the widget is, or is an ancestor of, the given widget. */
|
|
198
|
-
toContainElement: (received: unknown, descendant: Gtk.
|
|
198
|
+
toContainElement: (received: unknown, descendant: Gtk.Accessible | null) => MatcherResult;
|
|
199
199
|
/** Asserts the widget comes before the given widget in the widget tree, neither containing the other. */
|
|
200
|
-
toAppearBefore: (received: unknown, other: Gtk.
|
|
200
|
+
toAppearBefore: (received: unknown, other: Gtk.Accessible) => MatcherResult;
|
|
201
201
|
/** Asserts the widget comes after the given widget in the widget tree, neither containing the other. */
|
|
202
|
-
toAppearAfter: (received: unknown, other: Gtk.
|
|
202
|
+
toAppearAfter: (received: unknown, other: Gtk.Accessible) => MatcherResult;
|
|
203
203
|
/** Asserts at least one widget under the received one matches the `ByRole` query. */
|
|
204
204
|
toContainAnyByRole: ContainmentMatcher<RoleQueryArgs>;
|
|
205
205
|
/** Asserts exactly one widget under the received one matches the `ByRole` query. */
|
|
@@ -783,14 +783,14 @@ function toBePartiallyPressed(received: unknown): MatcherResult {
|
|
|
783
783
|
return stateResult(widget, "partially pressed", state === MIXED_TRISTATE);
|
|
784
784
|
}
|
|
785
785
|
|
|
786
|
-
function toAppearBefore(received: unknown, other: Gtk.
|
|
786
|
+
function toAppearBefore(received: unknown, other: Gtk.Accessible): MatcherResult {
|
|
787
787
|
const widget = asWidget(received, "toAppearBefore");
|
|
788
788
|
const target = asWidget(other, "toAppearBefore");
|
|
789
789
|
|
|
790
790
|
return orderResult(widget, target, "before", isAppearingBefore(widget, target));
|
|
791
791
|
}
|
|
792
792
|
|
|
793
|
-
function toAppearAfter(received: unknown, other: Gtk.
|
|
793
|
+
function toAppearAfter(received: unknown, other: Gtk.Accessible): MatcherResult {
|
|
794
794
|
const widget = asWidget(received, "toAppearAfter");
|
|
795
795
|
const target = asWidget(other, "toAppearAfter");
|
|
796
796
|
|
|
@@ -922,15 +922,16 @@ function toHaveRole(received: unknown, expected: Gtk.AccessibleRole): MatcherRes
|
|
|
922
922
|
};
|
|
923
923
|
}
|
|
924
924
|
|
|
925
|
-
function toContainElement(received: unknown, descendant: Gtk.
|
|
925
|
+
function toContainElement(received: unknown, descendant: Gtk.Accessible | null): MatcherResult {
|
|
926
926
|
const widget = asWidget(received, "toContainElement");
|
|
927
|
-
const
|
|
927
|
+
const target = descendant === null ? null : asWidget(descendant, "toContainElement");
|
|
928
|
+
const isPass = target !== null && (target === widget || target.isAncestor(widget));
|
|
928
929
|
|
|
929
930
|
return {
|
|
930
931
|
pass: isPass,
|
|
931
932
|
message: () =>
|
|
932
933
|
`expected widget ${negationPrefix(isPass)}to contain ` +
|
|
933
|
-
`${
|
|
934
|
+
`${target === null ? "null" : describeWidget(target)}\n${describeWidget(widget)}`,
|
|
934
935
|
};
|
|
935
936
|
}
|
|
936
937
|
|
|
@@ -1037,9 +1038,9 @@ declare module "@vitest/expect" {
|
|
|
1037
1038
|
toHaveFocus(): void;
|
|
1038
1039
|
toHaveValue(expected?: number | string): void;
|
|
1039
1040
|
toHaveRole(expected: Gtk.AccessibleRole): void;
|
|
1040
|
-
toContainElement(descendant: Gtk.
|
|
1041
|
-
toAppearBefore(other: Gtk.
|
|
1042
|
-
toAppearAfter(other: Gtk.
|
|
1041
|
+
toContainElement(descendant: Gtk.Accessible | null): void;
|
|
1042
|
+
toAppearBefore(other: Gtk.Accessible): void;
|
|
1043
|
+
toAppearAfter(other: Gtk.Accessible): void;
|
|
1043
1044
|
toContainAnyByRole(...args: RoleQueryArgs): void;
|
|
1044
1045
|
toContainOneByRole(...args: RoleQueryArgs): void;
|
|
1045
1046
|
toContainAnyByText(...args: TextQueryArgs): void;
|
package/src/queries.ts
CHANGED
|
@@ -39,7 +39,7 @@ type BuiltinQueries = QueryFamilies<[container: Container]>;
|
|
|
39
39
|
const roleQueries = nameQueryFamily(
|
|
40
40
|
"Role",
|
|
41
41
|
queryAllByRole,
|
|
42
|
-
buildQueries<[role: Gtk.AccessibleRole, options?: ByRoleOptions]>(
|
|
42
|
+
buildQueries<[role: Gtk.AccessibleRole, options?: ByRoleOptions<Gtk.Widget>], Gtk.Widget>(
|
|
43
43
|
queryAllByRole,
|
|
44
44
|
(container, matches, role, options) =>
|
|
45
45
|
multipleFoundError(container, { queryType: "role", role, options }, matches),
|
|
@@ -50,7 +50,7 @@ const roleQueries = nameQueryFamily(
|
|
|
50
50
|
const labelTextQueries = nameQueryFamily(
|
|
51
51
|
"LabelText",
|
|
52
52
|
queryAllByLabelText,
|
|
53
|
-
buildQueries<[text: Matcher, options?: MatcherOptions]>(
|
|
53
|
+
buildQueries<[text: Matcher, options?: MatcherOptions<Gtk.Widget>], Gtk.Widget>(
|
|
54
54
|
queryAllByLabelText,
|
|
55
55
|
(container, matches, text) => multipleFoundError(container, { queryType: "labelText", text }, matches),
|
|
56
56
|
(container, text) => notFoundError(container, { queryType: "labelText", text }),
|
|
@@ -60,7 +60,7 @@ const labelTextQueries = nameQueryFamily(
|
|
|
60
60
|
const textQueries = nameQueryFamily(
|
|
61
61
|
"Text",
|
|
62
62
|
queryAllByText,
|
|
63
|
-
buildQueries<[text: Matcher, options?: MatcherOptions]>(
|
|
63
|
+
buildQueries<[text: Matcher, options?: MatcherOptions<Gtk.Widget>], Gtk.Widget>(
|
|
64
64
|
queryAllByText,
|
|
65
65
|
(container, matches, text) => multipleFoundError(container, { queryType: "text", text }, matches),
|
|
66
66
|
(container, text) => notFoundError(container, { queryType: "text", text }),
|
|
@@ -70,7 +70,7 @@ const textQueries = nameQueryFamily(
|
|
|
70
70
|
const nameQueries = nameQueryFamily(
|
|
71
71
|
"Name",
|
|
72
72
|
queryAllByName,
|
|
73
|
-
buildQueries<[name: Matcher, options?: MatcherOptions]>(
|
|
73
|
+
buildQueries<[name: Matcher, options?: MatcherOptions<Gtk.Widget>], Gtk.Widget>(
|
|
74
74
|
queryAllByName,
|
|
75
75
|
(container, matches, name) => multipleFoundError(container, { queryType: "name", name }, matches),
|
|
76
76
|
(container, name) => notFoundError(container, { queryType: "name", name }),
|
|
@@ -80,7 +80,7 @@ const nameQueries = nameQueryFamily(
|
|
|
80
80
|
const placeholderTextQueries = nameQueryFamily(
|
|
81
81
|
"PlaceholderText",
|
|
82
82
|
queryAllByPlaceholderText,
|
|
83
|
-
buildQueries<[text: Matcher, options?: MatcherOptions]>(
|
|
83
|
+
buildQueries<[text: Matcher, options?: MatcherOptions<Gtk.Widget>], Gtk.Widget>(
|
|
84
84
|
queryAllByPlaceholderText,
|
|
85
85
|
(container, matches, text) => multipleFoundError(container, { queryType: "placeholderText", text }, matches),
|
|
86
86
|
(container, text) => notFoundError(container, { queryType: "placeholderText", text }),
|
|
@@ -90,7 +90,7 @@ const placeholderTextQueries = nameQueryFamily(
|
|
|
90
90
|
const displayValueQueries = nameQueryFamily(
|
|
91
91
|
"DisplayValue",
|
|
92
92
|
queryAllByDisplayValue,
|
|
93
|
-
buildQueries<[value: Matcher, options?: MatcherOptions]>(
|
|
93
|
+
buildQueries<[value: Matcher, options?: MatcherOptions<Gtk.Widget>], Gtk.Widget>(
|
|
94
94
|
queryAllByDisplayValue,
|
|
95
95
|
(container, matches, value) => multipleFoundError(container, { queryType: "displayValue", value }, matches),
|
|
96
96
|
(container, value) => notFoundError(container, { queryType: "displayValue", value }),
|
|
@@ -284,25 +284,27 @@ const hasMatchingAccessibleStates = (widget: Gtk.Widget, options: ByRoleOptions)
|
|
|
284
284
|
hasMatchingDescriptionState(widget, options) &&
|
|
285
285
|
hasMatchingValueState(widget, options);
|
|
286
286
|
|
|
287
|
-
const isMatchingWidgetType =
|
|
287
|
+
const isMatchingWidgetType = <T extends Gtk.Accessible>(
|
|
288
|
+
widget: Gtk.Widget,
|
|
289
|
+
options?: MatcherOptions<T>,
|
|
290
|
+
): widget is Gtk.Widget & T =>
|
|
288
291
|
options?.as === undefined || widget instanceof options.as;
|
|
289
292
|
|
|
290
|
-
const
|
|
293
|
+
const hasMatchingAccessibleOptions = (widget: Gtk.Widget, options?: ByRoleOptions): boolean => {
|
|
291
294
|
if (!options) {
|
|
292
295
|
return true;
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
return (
|
|
296
|
-
isMatchingWidgetType(widget, options) &&
|
|
297
299
|
hasMatchingAccessibleName(widget, options) &&
|
|
298
300
|
hasMatchingAccessibleStates(widget, options)
|
|
299
301
|
);
|
|
300
302
|
};
|
|
301
303
|
|
|
302
|
-
function nameQueryFamily<Args extends unknown[]>(
|
|
304
|
+
function nameQueryFamily<Args extends unknown[], Element extends Gtk.Accessible>(
|
|
303
305
|
suffix: string,
|
|
304
|
-
queryAllBy: QueryAllBy<Args>,
|
|
305
|
-
built: BuiltQueries<Args>,
|
|
306
|
+
queryAllBy: QueryAllBy<Args, Element>,
|
|
307
|
+
built: BuiltQueries<Args, Element>,
|
|
306
308
|
): Record<string, unknown> {
|
|
307
309
|
const [queryBy, getAllBy, getBy, findAllBy, findBy] = built;
|
|
308
310
|
|
|
@@ -325,12 +327,12 @@ function nameQueryFamily<Args extends unknown[]>(
|
|
|
325
327
|
* @param options Additional accessible name, state, and value constraints.
|
|
326
328
|
* @returns Every matching widget, or an empty array when none match.
|
|
327
329
|
*/
|
|
328
|
-
function queryAllByRole(
|
|
330
|
+
function queryAllByRole<T extends Gtk.Accessible = Gtk.Widget>(
|
|
329
331
|
container: Container,
|
|
330
332
|
role: Gtk.AccessibleRole,
|
|
331
|
-
options?: ByRoleOptions
|
|
332
|
-
):
|
|
333
|
-
|
|
333
|
+
options?: ByRoleOptions<T>,
|
|
334
|
+
): T[] {
|
|
335
|
+
const matches = findAll(container, (widget) => {
|
|
334
336
|
if (widget.getAccessibleRole() !== role) {
|
|
335
337
|
return false;
|
|
336
338
|
}
|
|
@@ -339,8 +341,14 @@ function queryAllByRole(
|
|
|
339
341
|
return false;
|
|
340
342
|
}
|
|
341
343
|
|
|
342
|
-
|
|
344
|
+
if (!isMatchingWidgetType(widget, options)) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return hasMatchingAccessibleOptions(widget, options);
|
|
343
349
|
});
|
|
350
|
+
|
|
351
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
344
352
|
}
|
|
345
353
|
|
|
346
354
|
const collectMnemonicMatch = (
|
|
@@ -394,14 +402,18 @@ const collectLabelMatches = (
|
|
|
394
402
|
* @param options Text matching options.
|
|
395
403
|
* @returns Every matching widget, or an empty array when none match.
|
|
396
404
|
*/
|
|
397
|
-
function queryAllByLabelText
|
|
405
|
+
function queryAllByLabelText<T extends Gtk.Accessible = Gtk.Widget>(
|
|
406
|
+
container: Container,
|
|
407
|
+
text: Matcher,
|
|
408
|
+
options?: MatcherOptions<T>,
|
|
409
|
+
): T[] {
|
|
398
410
|
const results: Set<Gtk.Widget> = new Set();
|
|
399
411
|
|
|
400
412
|
for (const widget of traverse(container)) {
|
|
401
413
|
collectLabelMatches(results, widget, text, options);
|
|
402
414
|
}
|
|
403
415
|
|
|
404
|
-
return [...results].filter((widget) => isMatchingWidgetType(widget, options));
|
|
416
|
+
return [...results].filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
405
417
|
}
|
|
406
418
|
|
|
407
419
|
/**
|
|
@@ -411,12 +423,18 @@ function queryAllByLabelText(container: Container, text: Matcher, options?: Matc
|
|
|
411
423
|
* @param options Text matching options.
|
|
412
424
|
* @returns Every matching widget, or an empty array when none match.
|
|
413
425
|
*/
|
|
414
|
-
function queryAllByText
|
|
415
|
-
|
|
426
|
+
function queryAllByText<T extends Gtk.Accessible = Gtk.Widget>(
|
|
427
|
+
container: Container,
|
|
428
|
+
text: Matcher,
|
|
429
|
+
options?: MatcherOptions<T>,
|
|
430
|
+
): T[] {
|
|
431
|
+
const matches = findAll(
|
|
416
432
|
container,
|
|
417
433
|
(widget) =>
|
|
418
434
|
isMatchingWidgetType(widget, options) && isTextMatch(getWidgetLabelText(widget), text, widget, options),
|
|
419
435
|
);
|
|
436
|
+
|
|
437
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
420
438
|
}
|
|
421
439
|
|
|
422
440
|
/**
|
|
@@ -426,11 +444,17 @@ function queryAllByText(container: Container, text: Matcher, options?: MatcherOp
|
|
|
426
444
|
* @param options Text matching options.
|
|
427
445
|
* @returns Every matching widget, or an empty array when none match.
|
|
428
446
|
*/
|
|
429
|
-
function queryAllByName
|
|
430
|
-
|
|
447
|
+
function queryAllByName<T extends Gtk.Accessible = Gtk.Widget>(
|
|
448
|
+
container: Container,
|
|
449
|
+
name: Matcher,
|
|
450
|
+
options?: MatcherOptions<T>,
|
|
451
|
+
): T[] {
|
|
452
|
+
const matches = findAll(
|
|
431
453
|
container,
|
|
432
454
|
(widget) => isMatchingWidgetType(widget, options) && isTextMatch(getWidgetName(widget), name, widget, options),
|
|
433
455
|
);
|
|
456
|
+
|
|
457
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
434
458
|
}
|
|
435
459
|
|
|
436
460
|
/**
|
|
@@ -440,17 +464,19 @@ function queryAllByName(container: Container, name: Matcher, options?: MatcherOp
|
|
|
440
464
|
* @param options Text matching options.
|
|
441
465
|
* @returns Every matching widget, or an empty array when none match.
|
|
442
466
|
*/
|
|
443
|
-
function queryAllByPlaceholderText(
|
|
467
|
+
function queryAllByPlaceholderText<T extends Gtk.Accessible = Gtk.Widget>(
|
|
444
468
|
container: Container,
|
|
445
469
|
text: Matcher,
|
|
446
|
-
options?: MatcherOptions
|
|
447
|
-
):
|
|
448
|
-
|
|
470
|
+
options?: MatcherOptions<T>,
|
|
471
|
+
): T[] {
|
|
472
|
+
const matches = findAll(
|
|
449
473
|
container,
|
|
450
474
|
(widget) =>
|
|
451
475
|
isMatchingWidgetType(widget, options) &&
|
|
452
476
|
isTextMatch(getWidgetPlaceholderText(widget), text, widget, options),
|
|
453
477
|
);
|
|
478
|
+
|
|
479
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
454
480
|
}
|
|
455
481
|
|
|
456
482
|
/**
|
|
@@ -460,13 +486,19 @@ function queryAllByPlaceholderText(
|
|
|
460
486
|
* @param options Text matching options.
|
|
461
487
|
* @returns Every matching widget, or an empty array when none match.
|
|
462
488
|
*/
|
|
463
|
-
function queryAllByDisplayValue
|
|
464
|
-
|
|
489
|
+
function queryAllByDisplayValue<T extends Gtk.Accessible = Gtk.Widget>(
|
|
490
|
+
container: Container,
|
|
491
|
+
value: Matcher,
|
|
492
|
+
options?: MatcherOptions<T>,
|
|
493
|
+
): T[] {
|
|
494
|
+
const matches = findAll(
|
|
465
495
|
container,
|
|
466
496
|
(widget) =>
|
|
467
497
|
isMatchingWidgetType(widget, options) &&
|
|
468
498
|
isTextMatch(getWidgetDisplayValue(widget), value, widget, options),
|
|
469
499
|
);
|
|
500
|
+
|
|
501
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
470
502
|
}
|
|
471
503
|
|
|
472
504
|
export {
|
package/src/query-helpers.ts
CHANGED
|
@@ -57,18 +57,20 @@ const readObjectProperty = (widget: Gtk.Widget, property: string): string | null
|
|
|
57
57
|
* @param options Text matching options.
|
|
58
58
|
* @returns Every matching widget, or an empty array when none match.
|
|
59
59
|
*/
|
|
60
|
-
function queryAllByObjectProperty(
|
|
60
|
+
function queryAllByObjectProperty<T extends Gtk.Accessible = Gtk.Widget>(
|
|
61
61
|
property: string,
|
|
62
62
|
container: Container,
|
|
63
63
|
text: Matcher,
|
|
64
|
-
options?: MatcherOptions
|
|
65
|
-
):
|
|
66
|
-
|
|
64
|
+
options?: MatcherOptions<T>,
|
|
65
|
+
): T[] {
|
|
66
|
+
const matches = findAll(
|
|
67
67
|
container,
|
|
68
68
|
(widget) =>
|
|
69
69
|
isMatchingWidgetType(widget, options) &&
|
|
70
70
|
isTextMatch(readObjectProperty(widget, property), text, widget, options),
|
|
71
71
|
);
|
|
72
|
+
|
|
73
|
+
return matches.filter((widget): widget is Gtk.Widget & T => isMatchingWidgetType(widget, options));
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
/**
|
|
@@ -82,12 +84,12 @@ function queryAllByObjectProperty(
|
|
|
82
84
|
* @returns The matching widget, or null when none match.
|
|
83
85
|
* @throws When more than one widget matches.
|
|
84
86
|
*/
|
|
85
|
-
function queryByObjectProperty(
|
|
87
|
+
function queryByObjectProperty<T extends Gtk.Accessible = Gtk.Widget>(
|
|
86
88
|
property: string,
|
|
87
89
|
container: Container,
|
|
88
90
|
text: Matcher,
|
|
89
|
-
options?: MatcherOptions
|
|
90
|
-
):
|
|
91
|
+
options?: MatcherOptions<T>,
|
|
92
|
+
): T | null {
|
|
91
93
|
const matches = queryAllByObjectProperty(property, container, text, options);
|
|
92
94
|
|
|
93
95
|
if (matches.length > 1) {
|
package/src/render.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import { clearScreen, setScreen } from "./screen.js";
|
|
|
21
21
|
import { captureScreen } from "./screenshot.js";
|
|
22
22
|
import { type Container, roots, TOPLEVELS } from "./traversal.js";
|
|
23
23
|
import { resetClipboard } from "./user-event/index.js";
|
|
24
|
+
import { requireWidget } from "./widget-target.js";
|
|
24
25
|
import { findPresentedWindowFailure, findRenderedWindowFailure, mappedToplevels } from "./window-state.js";
|
|
25
26
|
import { within } from "./within.js";
|
|
26
27
|
|
|
@@ -125,6 +126,10 @@ const resolveContainer = (container: RenderOptions["container"]): ResolvedContai
|
|
|
125
126
|
return { containerInfo: container, window: null };
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
if (container !== undefined) {
|
|
130
|
+
requireWidget(container);
|
|
131
|
+
}
|
|
132
|
+
|
|
128
133
|
const window = createHarnessWindow();
|
|
129
134
|
window.setDecorated(false);
|
|
130
135
|
|
package/src/role-helpers.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { sortStringsBy } from "@gtkx/utils";
|
|
|
3
3
|
import { type Container, traverse } from "./traversal.js";
|
|
4
4
|
import { getWidgetAccessibleName, getWidgetLevel } from "./widget-accessible-properties.js";
|
|
5
5
|
import { getTypeTag } from "./widget-getters.js";
|
|
6
|
+
import { requireWidget } from "./widget-target.js";
|
|
6
7
|
|
|
7
8
|
const ROLE_NAMES_BY_VALUE = enumNamesByValue(Gtk.AccessibleRole);
|
|
8
9
|
|
|
@@ -125,6 +126,7 @@ const logRoles = (container: Container): void => {
|
|
|
125
126
|
*
|
|
126
127
|
* @param widget The widget to read the level from.
|
|
127
128
|
*/
|
|
128
|
-
const computeHeadingLevel = (widget: Gtk.
|
|
129
|
+
const computeHeadingLevel = (widget: Gtk.Accessible): number | undefined =>
|
|
130
|
+
getWidgetLevel(requireWidget(widget)) ?? undefined;
|
|
129
131
|
|
|
130
132
|
export { computeHeadingLevel, formatRole, formatRoleList, getRoles, prettyRoles, logRoles };
|