@gtkx/testing 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/act.d.ts +6 -6
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js +34 -26
- package/dist/act.js.map +1 -1
- package/dist/bound-queries.d.ts +5 -5
- package/dist/bound-queries.d.ts.map +1 -1
- package/dist/bound-queries.js.map +1 -1
- package/dist/build-queries.d.ts +5 -5
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +21 -9
- package/dist/build-queries.js.map +1 -1
- package/dist/cleanup-registry.d.ts +4 -3
- package/dist/cleanup-registry.d.ts.map +1 -1
- package/dist/cleanup-registry.js +13 -3
- package/dist/cleanup-registry.js.map +1 -1
- package/dist/config.d.ts +5 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -9
- package/dist/config.js.map +1 -1
- package/dist/editable.d.ts +6 -5
- package/dist/editable.d.ts.map +1 -1
- package/dist/editable.js +7 -5
- package/dist/editable.js.map +1 -1
- package/dist/errors.d.ts +7 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +64 -48
- package/dist/errors.js.map +1 -1
- package/dist/fire-event.d.ts +2 -1
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +2 -1
- package/dist/fire-event.js.map +1 -1
- package/dist/frame-sync.d.ts +2 -1
- package/dist/frame-sync.d.ts.map +1 -1
- package/dist/frame-sync.js +28 -21
- package/dist/frame-sync.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +99 -35
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +314 -63
- package/dist/matchers.js.map +1 -1
- package/dist/normalize.d.ts +10 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +20 -0
- package/dist/normalize.js.map +1 -0
- package/dist/pretty-widget.d.ts +6 -4
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +64 -24
- package/dist/pretty-widget.js.map +1 -1
- package/dist/queries.d.ts +129 -90
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +235 -164
- package/dist/queries.js.map +1 -1
- package/dist/register-test-hooks.js +2 -1
- package/dist/register-test-hooks.js.map +1 -1
- package/dist/render-hook.d.ts +3 -2
- package/dist/render-hook.d.ts.map +1 -1
- package/dist/render-hook.js +2 -1
- package/dist/render-hook.js.map +1 -1
- package/dist/render.d.ts +3 -2
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +25 -19
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +6 -5
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +16 -12
- package/dist/role-helpers.js.map +1 -1
- package/dist/screen.d.ts +4 -3
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +13 -11
- package/dist/screen.js.map +1 -1
- package/dist/screenshot.d.ts +4 -3
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +43 -36
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +11 -6
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js +10 -5
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +7 -7
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +17 -11
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +65 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +5 -3
- package/dist/user-event/adjustment.d.ts.map +1 -1
- package/dist/user-event/adjustment.js +6 -4
- package/dist/user-event/adjustment.js.map +1 -1
- package/dist/user-event/click.d.ts +6 -5
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +29 -21
- package/dist/user-event/click.js.map +1 -1
- package/dist/user-event/controller.d.ts +7 -5
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +16 -6
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/dispatch.d.ts +3 -3
- package/dist/user-event/dispatch.d.ts.map +1 -1
- package/dist/user-event/dispatch.js +3 -2
- package/dist/user-event/dispatch.js.map +1 -1
- package/dist/user-event/event-wrapper.d.ts +2 -1
- package/dist/user-event/event-wrapper.d.ts.map +1 -1
- package/dist/user-event/event-wrapper.js +27 -16
- package/dist/user-event/event-wrapper.js.map +1 -1
- package/dist/user-event/gesture.d.ts +18 -14
- package/dist/user-event/gesture.d.ts.map +1 -1
- package/dist/user-event/gesture.js +70 -37
- package/dist/user-event/gesture.js.map +1 -1
- package/dist/user-event/index.d.ts +17 -10
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +7 -3
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/keyboard.d.ts +4 -3
- package/dist/user-event/keyboard.d.ts.map +1 -1
- package/dist/user-event/keyboard.js +89 -58
- package/dist/user-event/keyboard.js.map +1 -1
- package/dist/user-event/pointer.d.ts +7 -3
- package/dist/user-event/pointer.d.ts.map +1 -1
- package/dist/user-event/pointer.js +4 -1
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +3 -2
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +26 -27
- package/dist/user-event/selection.js.map +1 -1
- package/dist/user-event/state.d.ts +3 -2
- package/dist/user-event/state.d.ts.map +1 -1
- package/dist/user-event/state.js +2 -1
- package/dist/user-event/state.js.map +1 -1
- package/dist/user-event/text.d.ts +8 -7
- package/dist/user-event/text.d.ts.map +1 -1
- package/dist/user-event/text.js +39 -33
- package/dist/user-event/text.js.map +1 -1
- package/dist/wait-for.d.ts +5 -5
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js +45 -30
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +36 -25
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +215 -68
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/within.d.ts +2 -1
- package/dist/within.d.ts.map +1 -1
- package/dist/within.js +2 -1
- package/dist/within.js.map +1 -1
- package/package.json +11 -9
- package/src/act.ts +44 -35
- package/src/bound-queries.ts +6 -6
- package/src/build-queries.ts +84 -43
- package/src/cleanup-registry.ts +18 -5
- package/src/config.ts +16 -12
- package/src/editable.ts +13 -6
- package/src/errors.ts +106 -45
- package/src/fire-event.ts +3 -1
- package/src/frame-sync.ts +38 -21
- package/src/index.ts +13 -3
- package/src/matchers.ts +568 -114
- package/src/normalize.ts +28 -0
- package/src/pretty-widget.ts +114 -42
- package/src/queries.ts +418 -256
- package/src/register-test-hooks.ts +4 -1
- package/src/render-hook.tsx +10 -4
- package/src/render.tsx +59 -34
- package/src/role-helpers.ts +29 -16
- package/src/screen.ts +17 -12
- package/src/screenshot.ts +61 -38
- package/src/suggestions.ts +29 -10
- package/src/traversal.ts +35 -15
- package/src/types.ts +111 -32
- package/src/user-event/adjustment.ts +23 -10
- package/src/user-event/click.ts +49 -20
- package/src/user-event/controller.ts +41 -7
- package/src/user-event/dispatch.ts +4 -2
- package/src/user-event/event-wrapper.ts +42 -16
- package/src/user-event/gesture.ts +135 -50
- package/src/user-event/index.ts +19 -12
- package/src/user-event/keyboard.ts +147 -61
- package/src/user-event/pointer.ts +20 -8
- package/src/user-event/selection.ts +69 -32
- package/src/user-event/state.ts +4 -2
- package/src/user-event/text.ts +73 -38
- package/src/wait-for.ts +77 -36
- package/src/widget-accessible-properties.ts +385 -79
- package/src/within.ts +8 -5
package/src/act.ts
CHANGED
|
@@ -4,65 +4,74 @@ declare global {
|
|
|
4
4
|
var IS_REACT_ACT_ENVIRONMENT: boolean | undefined;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
type ActCallback<T> = () => T | PromiseLike<T>;
|
|
8
|
+
type ActImplementation = <T>(callback: ActCallback<T>) => PromiseLike<T>;
|
|
9
|
+
|
|
10
|
+
const actImplementation: ActImplementation = reactAct;
|
|
11
|
+
/**
|
|
12
|
+
* Runs a callback inside React's act environment, flushing pending state
|
|
13
|
+
* updates and effects, and resolves once the work has settled.
|
|
14
|
+
*
|
|
15
|
+
* @param callback Work to run within the act environment.
|
|
16
|
+
* @returns A promise that resolves with the callback's result after updates flush.
|
|
17
|
+
*/
|
|
18
|
+
const act: ActImplementation = withGlobalActEnvironment(actImplementation);
|
|
19
|
+
|
|
7
20
|
const getGlobalThis = (): typeof globalThis => {
|
|
8
|
-
if (typeof globalThis !== "undefined")
|
|
21
|
+
if (typeof globalThis !== "undefined") {
|
|
22
|
+
return globalThis;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
throw new Error("unable to locate global object");
|
|
10
26
|
};
|
|
11
27
|
|
|
12
|
-
|
|
28
|
+
const getIsReactActEnvironment = (): boolean | undefined => getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
|
|
13
29
|
|
|
14
|
-
|
|
30
|
+
const setIsReactActEnvironment = (value: boolean | undefined): void => {
|
|
15
31
|
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = value;
|
|
16
32
|
};
|
|
17
33
|
|
|
18
|
-
type ActCallback<T> = () => T | PromiseLike<T>;
|
|
19
|
-
type ActImplementation = <T>(callback: ActCallback<T>) => PromiseLike<T>;
|
|
20
|
-
|
|
21
34
|
const isThenable = <T>(value: unknown): value is PromiseLike<T> =>
|
|
22
35
|
value !== null && typeof value === "object" && typeof (value as PromiseLike<T>).then === "function";
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
const restoreAfter = async <T>(result: PromiseLike<T>, previous: boolean | undefined): Promise<T> => {
|
|
38
|
+
try {
|
|
39
|
+
return await result;
|
|
40
|
+
} finally {
|
|
41
|
+
setIsReactActEnvironment(previous);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const runWithActEnvironment = <T>(isForced: boolean, fn: () => T | PromiseLike<T>): Promise<T> => {
|
|
25
46
|
const previousActEnvironment = getIsReactActEnvironment();
|
|
26
|
-
setIsReactActEnvironment(
|
|
47
|
+
setIsReactActEnvironment(isForced);
|
|
48
|
+
|
|
27
49
|
try {
|
|
28
|
-
const result = fn();
|
|
50
|
+
const result: T | PromiseLike<T> = fn();
|
|
51
|
+
|
|
29
52
|
if (isThenable<T>(result)) {
|
|
30
|
-
return
|
|
31
|
-
(value) => {
|
|
32
|
-
setIsReactActEnvironment(previousActEnvironment);
|
|
33
|
-
return value;
|
|
34
|
-
},
|
|
35
|
-
(error) => {
|
|
36
|
-
setIsReactActEnvironment(previousActEnvironment);
|
|
37
|
-
throw error;
|
|
38
|
-
},
|
|
39
|
-
);
|
|
53
|
+
return restoreAfter(result, previousActEnvironment);
|
|
40
54
|
}
|
|
55
|
+
|
|
41
56
|
setIsReactActEnvironment(previousActEnvironment);
|
|
42
|
-
|
|
57
|
+
|
|
58
|
+
return Promise.resolve(result);
|
|
43
59
|
} catch (error) {
|
|
44
60
|
setIsReactActEnvironment(previousActEnvironment);
|
|
45
61
|
throw error;
|
|
46
62
|
}
|
|
47
63
|
};
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
(
|
|
51
|
-
<T>(callback: ActCallback<T>): PromiseLike<T> => {
|
|
65
|
+
function withGlobalActEnvironment(actImplementation: ActImplementation): ActImplementation {
|
|
66
|
+
return <T>(callback: ActCallback<T>): PromiseLike<T> => {
|
|
52
67
|
const settled = runWithActEnvironment(true, () => actImplementation(() => callback()));
|
|
68
|
+
|
|
53
69
|
return Promise.resolve(settled);
|
|
54
70
|
};
|
|
71
|
+
}
|
|
55
72
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* Runs a callback inside React's act environment, flushing pending state
|
|
60
|
-
* updates and effects, and resolves once the work has settled.
|
|
61
|
-
*
|
|
62
|
-
* @param callback Work to run within the act environment.
|
|
63
|
-
* @returns A promise that resolves with the callback's result after updates flush.
|
|
64
|
-
*/
|
|
65
|
-
export const act: ActImplementation = withGlobalActEnvironment(actImplementation);
|
|
73
|
+
const runInAct = async (callback: () => unknown): Promise<void> => {
|
|
74
|
+
await act(() => callback());
|
|
75
|
+
};
|
|
66
76
|
|
|
67
|
-
export
|
|
68
|
-
Promise.resolve(act(() => callback())).then(() => undefined);
|
|
77
|
+
export { act, getIsReactActEnvironment, setIsReactActEnvironment, runWithActEnvironment, runInAct };
|
package/src/bound-queries.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type { builtinQueries } from "./queries.js";
|
|
4
3
|
import type { Container } from "./traversal.js";
|
|
5
|
-
import type { BoundCustomQueries, DebugUtilities, QueryMap } from "./types.js";
|
|
4
|
+
import type { BoundCustomQueries, DebugUtilities, QueryFamilies, QueryMap } from "./types.js";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* The set of built-in queries (getBy, findBy, queryBy, and their All variants)
|
|
9
8
|
* bound to a specific container.
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type BoundQueries = QueryFamilies<[]>;
|
|
13
11
|
/**
|
|
14
12
|
* The global screen object's shape: built-in queries scoped to the current
|
|
15
13
|
* toplevels, plus debug utilities.
|
|
16
14
|
*/
|
|
17
|
-
|
|
15
|
+
type Screen = BoundQueries & DebugUtilities;
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* The result of a render call: queries scoped to the rendered tree, debug
|
|
@@ -22,7 +20,7 @@ export type Screen = BoundQueries & DebugUtilities;
|
|
|
22
20
|
*
|
|
23
21
|
* @typeParam Q Custom queries provided at render time, merged with the built-in ones.
|
|
24
22
|
*/
|
|
25
|
-
|
|
23
|
+
type RenderResult<Q extends QueryMap = Record<never, never>> = BoundQueries &
|
|
26
24
|
BoundCustomQueries<Q> &
|
|
27
25
|
DebugUtilities & {
|
|
28
26
|
/** The top-level widget the element was rendered into. */
|
|
@@ -34,3 +32,5 @@ export type RenderResult<Q extends QueryMap = Record<never, never>> = BoundQueri
|
|
|
34
32
|
/** Re-renders the tree with a new element, reusing the same root. */
|
|
35
33
|
rerender: (element: ReactNode) => Promise<void>;
|
|
36
34
|
};
|
|
35
|
+
|
|
36
|
+
export { type BoundQueries, type Screen, type RenderResult };
|
package/src/build-queries.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { Container } from "./traversal.js";
|
|
3
|
+
import type { MatcherOptions, WaitForOptions } from "./types.js";
|
|
2
4
|
import { getConfig } from "./config.js";
|
|
3
5
|
import { runWithExpensiveErrorDiagnosticsDisabled, suggestionError } from "./errors.js";
|
|
4
6
|
import { getSuggestedQuery, type Method, type Variant } from "./suggestions.js";
|
|
5
|
-
import type { Container } from "./traversal.js";
|
|
6
|
-
import type { MatcherOptions, WaitForOptions } from "./types.js";
|
|
7
7
|
import { waitFor } from "./wait-for.js";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type QueryAllBy<Args extends unknown[]> = (container: Container, ...args: Args) => Gtk.Widget[];
|
|
10
10
|
|
|
11
11
|
type MultipleErrorBuilder<Args extends unknown[]> = (
|
|
12
12
|
container: Container,
|
|
13
13
|
matches: Gtk.Widget[],
|
|
14
14
|
...args: Args
|
|
15
15
|
) => Error;
|
|
16
|
+
|
|
16
17
|
type MissingErrorBuilder<Args extends unknown[]> = (container: Container, ...args: Args) => Error;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
type BuiltQueries<Args extends unknown[]> = {
|
|
19
20
|
queryBy: (container: Container, ...args: Args) => Gtk.Widget | null;
|
|
20
21
|
getAllBy: QueryAllBy<Args>;
|
|
21
22
|
getBy: (container: Container, ...args: Args) => Gtk.Widget;
|
|
@@ -23,20 +24,28 @@ export type BuiltQueries<Args extends unknown[]> = {
|
|
|
23
24
|
findBy: (container: Container, ...args: Args) => Promise<Gtk.Widget>;
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
type FindQuery<Args extends unknown[]> = (container: Container, ...args: Args) => unknown;
|
|
28
|
+
type SingleQuery<Args extends unknown[]> = (container: Container, ...args: Args) => Gtk.Widget | null;
|
|
29
|
+
|
|
26
30
|
const extractWaitForOptions = (args: unknown[]): WaitForOptions => {
|
|
27
|
-
const last = args
|
|
31
|
+
const last = args.at(-1);
|
|
32
|
+
|
|
28
33
|
if (last && typeof last === "object" && !(last instanceof RegExp)) {
|
|
29
34
|
const { timeout, interval, onTimeout } = last as WaitForOptions;
|
|
35
|
+
|
|
30
36
|
return { timeout, interval, onTimeout };
|
|
31
37
|
}
|
|
38
|
+
|
|
32
39
|
return {};
|
|
33
40
|
};
|
|
34
41
|
|
|
35
42
|
const extractSuggestOption = (args: unknown[]): boolean | undefined => {
|
|
36
|
-
const last = args
|
|
43
|
+
const last = args.at(-1);
|
|
44
|
+
|
|
37
45
|
if (last && typeof last === "object" && !(last instanceof RegExp)) {
|
|
38
46
|
return (last as MatcherOptions).suggest;
|
|
39
47
|
}
|
|
48
|
+
|
|
40
49
|
return undefined;
|
|
41
50
|
};
|
|
42
51
|
|
|
@@ -49,16 +58,18 @@ const maybeThrowSuggestion = (options: {
|
|
|
49
58
|
}): void => {
|
|
50
59
|
const { container, match, queryName, variant, suggest } = options;
|
|
51
60
|
const shouldSuggest = suggest ?? getConfig().throwSuggestions;
|
|
52
|
-
|
|
61
|
+
|
|
62
|
+
if (!shouldSuggest) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
53
65
|
|
|
54
66
|
const suggestion = getSuggestedQuery(match, variant);
|
|
67
|
+
|
|
55
68
|
if (suggestion && suggestion.queryName !== queryName) {
|
|
56
69
|
throw suggestionError(suggestion.toString(), container);
|
|
57
70
|
}
|
|
58
71
|
};
|
|
59
72
|
|
|
60
|
-
type FindQuery<Args extends unknown[]> = (container: Container, ...args: Args) => unknown;
|
|
61
|
-
|
|
62
73
|
const reRunForDiagnostics = <Args extends unknown[]>(
|
|
63
74
|
query: FindQuery<Args>,
|
|
64
75
|
container: Container,
|
|
@@ -68,8 +79,11 @@ const reRunForDiagnostics = <Args extends unknown[]>(
|
|
|
68
79
|
try {
|
|
69
80
|
query(container, ...args);
|
|
70
81
|
} catch (error) {
|
|
71
|
-
if (error instanceof Error)
|
|
82
|
+
if (error instanceof Error) {
|
|
83
|
+
return error;
|
|
84
|
+
}
|
|
72
85
|
}
|
|
86
|
+
|
|
73
87
|
return fallback;
|
|
74
88
|
};
|
|
75
89
|
|
|
@@ -79,8 +93,10 @@ const findOptions = <Args extends unknown[]>(
|
|
|
79
93
|
args: Args,
|
|
80
94
|
): WaitForOptions => {
|
|
81
95
|
const userOptions = extractWaitForOptions(args);
|
|
96
|
+
|
|
82
97
|
const onTimeout =
|
|
83
98
|
userOptions.onTimeout ?? ((fallback: Error) => reRunForDiagnostics(query, container, args, fallback));
|
|
99
|
+
|
|
84
100
|
return {
|
|
85
101
|
stackTraceError: new Error("STACK_TRACE_MESSAGE"),
|
|
86
102
|
onTimeout,
|
|
@@ -89,60 +105,80 @@ const findOptions = <Args extends unknown[]>(
|
|
|
89
105
|
};
|
|
90
106
|
};
|
|
91
107
|
|
|
92
|
-
type SingleQuery<Args extends unknown[]> = (container: Container, ...args: Args) => Gtk.Widget | null;
|
|
93
|
-
|
|
94
108
|
const singleFrom =
|
|
95
109
|
<Args extends unknown[]>(
|
|
96
110
|
allQuery: QueryAllBy<Args>,
|
|
97
111
|
getMultipleError: MultipleErrorBuilder<Args>,
|
|
98
112
|
): SingleQuery<Args> =>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
(container, ...args) => {
|
|
114
|
+
const matches = allQuery(container, ...args);
|
|
115
|
+
|
|
116
|
+
if (matches.length > 1) {
|
|
117
|
+
throw getMultipleError(container, matches, ...args);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return matches[0] ?? null;
|
|
121
|
+
};
|
|
104
122
|
|
|
105
123
|
const allOrThrow =
|
|
106
124
|
<Args extends unknown[]>(
|
|
107
125
|
allQuery: QueryAllBy<Args>,
|
|
108
126
|
getMissingError: MissingErrorBuilder<Args>,
|
|
109
127
|
): QueryAllBy<Args> =>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
(container, ...args) => {
|
|
129
|
+
const matches = allQuery(container, ...args);
|
|
130
|
+
|
|
131
|
+
if (matches.length === 0) {
|
|
132
|
+
throw getMissingError(container, ...args);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return matches;
|
|
136
|
+
};
|
|
115
137
|
|
|
116
138
|
const wrapSingleWithSuggestion =
|
|
117
139
|
<Args extends unknown[]>(query: SingleQuery<Args>, queryName: Method, variant: Variant): SingleQuery<Args> =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
(container, ...args) => {
|
|
141
|
+
const match = query(container, ...args);
|
|
142
|
+
|
|
143
|
+
if (match) {
|
|
144
|
+
maybeThrowSuggestion({ container, match, queryName, variant, suggest: extractSuggestOption(args) });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return match;
|
|
148
|
+
};
|
|
125
149
|
|
|
126
150
|
const wrapAllWithSuggestion =
|
|
127
151
|
<Args extends unknown[]>(query: QueryAllBy<Args>, queryName: Method, variant: Variant): QueryAllBy<Args> =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
(container, ...args) => {
|
|
153
|
+
const matches = query(container, ...args);
|
|
154
|
+
const [first] = matches;
|
|
155
|
+
|
|
156
|
+
if (first !== undefined) {
|
|
157
|
+
maybeThrowSuggestion({
|
|
158
|
+
container,
|
|
159
|
+
match: first,
|
|
160
|
+
queryName,
|
|
161
|
+
variant,
|
|
162
|
+
suggest: extractSuggestOption(args),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return matches;
|
|
167
|
+
};
|
|
136
168
|
|
|
137
169
|
const requireSingle =
|
|
138
170
|
<Args extends unknown[]>(query: SingleQuery<Args>, getMissingError: MissingErrorBuilder<Args>) =>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
(container: Container, ...args: Args): Gtk.Widget => {
|
|
172
|
+
const match = query(container, ...args);
|
|
173
|
+
|
|
174
|
+
if (match === null) {
|
|
175
|
+
throw getMissingError(container, ...args);
|
|
176
|
+
}
|
|
144
177
|
|
|
145
|
-
|
|
178
|
+
return match;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const buildQueries = <Args extends unknown[]>(
|
|
146
182
|
queryName: Method,
|
|
147
183
|
queryAllBy: QueryAllBy<Args>,
|
|
148
184
|
getMultipleError: MultipleErrorBuilder<Args>,
|
|
@@ -156,10 +192,13 @@ export const buildQueries = <Args extends unknown[]>(
|
|
|
156
192
|
queryName,
|
|
157
193
|
"query",
|
|
158
194
|
);
|
|
195
|
+
|
|
159
196
|
const getAllByWithSuggestion = wrapAllWithSuggestion(getAllBy, queryName, "getAll");
|
|
197
|
+
|
|
160
198
|
const getByWithSuggestion: (container: Container, ...args: Args) => Gtk.Widget = (container, ...args) => {
|
|
161
199
|
const match = getBy(container, ...args);
|
|
162
200
|
maybeThrowSuggestion({ container, match, queryName, variant: "get", suggest: extractSuggestOption(args) });
|
|
201
|
+
|
|
163
202
|
return match;
|
|
164
203
|
};
|
|
165
204
|
|
|
@@ -183,3 +222,5 @@ export const buildQueries = <Args extends unknown[]>(
|
|
|
183
222
|
findBy,
|
|
184
223
|
};
|
|
185
224
|
};
|
|
225
|
+
|
|
226
|
+
export { buildQueries, type QueryAllBy, type BuiltQueries };
|
package/src/cleanup-registry.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
type CleanupFunction = () => void | Promise<void>;
|
|
2
2
|
|
|
3
|
-
const cleanupQueue = new Set
|
|
3
|
+
const cleanupQueue: Set<CleanupFunction> = new Set();
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const addToCleanupQueue = (fn: CleanupFunction): void => {
|
|
6
6
|
cleanupQueue.add(fn);
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const runCleanup = async (): Promise<void> => {
|
|
10
|
+
const failures: unknown[] = [];
|
|
11
|
+
|
|
10
12
|
for (const fn of cleanupQueue) {
|
|
11
|
-
|
|
13
|
+
try {
|
|
14
|
+
await fn();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
failures.push(error);
|
|
17
|
+
}
|
|
12
18
|
}
|
|
19
|
+
|
|
13
20
|
cleanupQueue.clear();
|
|
21
|
+
|
|
22
|
+
if (failures.length > 0) {
|
|
23
|
+
throw new AggregateError(failures, "Cleanup failed");
|
|
24
|
+
}
|
|
14
25
|
};
|
|
26
|
+
|
|
27
|
+
export { addToCleanupQueue, runCleanup, type CleanupFunction };
|
package/src/config.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Container } from "./traversal.js";
|
|
|
5
5
|
* error formatting, and the timeouts used by async utilities and actionability
|
|
6
6
|
* checks.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
type Config = {
|
|
9
9
|
throwSuggestions: boolean;
|
|
10
10
|
|
|
11
11
|
/** Builds the error thrown when a query fails, given a message and optional container. */
|
|
@@ -22,13 +22,7 @@ export type Config = {
|
|
|
22
22
|
* Function form of a configuration update: receives the current config and
|
|
23
23
|
* returns the fields to override.
|
|
24
24
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const defaultGetElementError = (message: string, _container?: Container): Error => {
|
|
28
|
-
const error = new Error(message);
|
|
29
|
-
error.name = "GtkxElementError";
|
|
30
|
-
return error;
|
|
31
|
-
};
|
|
25
|
+
type ConfigFn = (existingConfig: Config) => Partial<Config>;
|
|
32
26
|
|
|
33
27
|
const defaultConfig: Config = {
|
|
34
28
|
throwSuggestions: false,
|
|
@@ -37,12 +31,16 @@ const defaultConfig: Config = {
|
|
|
37
31
|
actionabilityTimeout: 500,
|
|
38
32
|
};
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
const currentConfig: Config = { ...defaultConfig };
|
|
35
|
+
|
|
36
|
+
function defaultGetElementError(message: string): Error {
|
|
37
|
+
return new GtkxElementError(message);
|
|
38
|
+
}
|
|
41
39
|
|
|
42
40
|
/**
|
|
43
41
|
* Returns the current global testing configuration.
|
|
44
42
|
*/
|
|
45
|
-
|
|
43
|
+
const getConfig = (): Config => {
|
|
46
44
|
return currentConfig;
|
|
47
45
|
};
|
|
48
46
|
|
|
@@ -52,7 +50,13 @@ export const getConfig = (): Config => {
|
|
|
52
50
|
* @param newConfig Either a partial config object or a function that receives
|
|
53
51
|
* the current config and returns the fields to override.
|
|
54
52
|
*/
|
|
55
|
-
|
|
53
|
+
const configure = (newConfig: Partial<Config> | ConfigFn): void => {
|
|
56
54
|
const updates = typeof newConfig === "function" ? newConfig(currentConfig) : newConfig;
|
|
57
|
-
currentConfig
|
|
55
|
+
Object.assign(currentConfig, updates);
|
|
58
56
|
};
|
|
57
|
+
|
|
58
|
+
class GtkxElementError extends Error {
|
|
59
|
+
override name = "GtkxElementError";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { getConfig, configure, type Config, type ConfigFn };
|
package/src/editable.ts
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type EditableTarget = Gtk.Editable | Gtk.TextView;
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const EDITABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
|
|
6
6
|
Gtk.AccessibleRole.TEXT_BOX,
|
|
7
7
|
Gtk.AccessibleRole.SEARCH_BOX,
|
|
8
8
|
Gtk.AccessibleRole.SPIN_BUTTON,
|
|
9
9
|
]);
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const isEditable = (widget: unknown): widget is EditableTarget =>
|
|
12
12
|
widget instanceof Gtk.Editable || widget instanceof Gtk.TextView;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
if (!(widget instanceof Gtk.Editable))
|
|
14
|
+
const getEditableDelegate = (widget: Gtk.Widget): Gtk.Widget | null => {
|
|
15
|
+
if (!(widget instanceof Gtk.Editable)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
16
19
|
return widget.getDelegate();
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
const readTextViewBufferText = (widget: Gtk.TextView): string => {
|
|
20
23
|
const buffer = widget.getBuffer();
|
|
21
24
|
const [start, end] = buffer.getBounds();
|
|
25
|
+
|
|
22
26
|
return buffer.getText(start, end, true);
|
|
23
27
|
};
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
const readEditableText = (widget: EditableTarget): string => {
|
|
26
30
|
if (widget instanceof Gtk.TextView) {
|
|
27
31
|
return readTextViewBufferText(widget);
|
|
28
32
|
}
|
|
33
|
+
|
|
29
34
|
return widget.getText();
|
|
30
35
|
};
|
|
36
|
+
|
|
37
|
+
export { EDITABLE_ROLES, isEditable, getEditableDelegate, readEditableText, type EditableTarget };
|