@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.
Files changed (187) hide show
  1. package/dist/act.d.ts +6 -6
  2. package/dist/act.d.ts.map +1 -1
  3. package/dist/act.js +34 -26
  4. package/dist/act.js.map +1 -1
  5. package/dist/bound-queries.d.ts +5 -5
  6. package/dist/bound-queries.d.ts.map +1 -1
  7. package/dist/bound-queries.js.map +1 -1
  8. package/dist/build-queries.d.ts +5 -5
  9. package/dist/build-queries.d.ts.map +1 -1
  10. package/dist/build-queries.js +21 -9
  11. package/dist/build-queries.js.map +1 -1
  12. package/dist/cleanup-registry.d.ts +4 -3
  13. package/dist/cleanup-registry.d.ts.map +1 -1
  14. package/dist/cleanup-registry.js +13 -3
  15. package/dist/cleanup-registry.js.map +1 -1
  16. package/dist/config.d.ts +5 -4
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +11 -9
  19. package/dist/config.js.map +1 -1
  20. package/dist/editable.d.ts +6 -5
  21. package/dist/editable.d.ts.map +1 -1
  22. package/dist/editable.js +7 -5
  23. package/dist/editable.js.map +1 -1
  24. package/dist/errors.d.ts +7 -6
  25. package/dist/errors.d.ts.map +1 -1
  26. package/dist/errors.js +64 -48
  27. package/dist/errors.js.map +1 -1
  28. package/dist/fire-event.d.ts +2 -1
  29. package/dist/fire-event.d.ts.map +1 -1
  30. package/dist/fire-event.js +2 -1
  31. package/dist/fire-event.js.map +1 -1
  32. package/dist/frame-sync.d.ts +2 -1
  33. package/dist/frame-sync.d.ts.map +1 -1
  34. package/dist/frame-sync.js +28 -21
  35. package/dist/frame-sync.js.map +1 -1
  36. package/dist/index.d.ts +9 -6
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +5 -3
  39. package/dist/index.js.map +1 -1
  40. package/dist/matchers.d.ts +99 -35
  41. package/dist/matchers.d.ts.map +1 -1
  42. package/dist/matchers.js +314 -63
  43. package/dist/matchers.js.map +1 -1
  44. package/dist/normalize.d.ts +10 -0
  45. package/dist/normalize.d.ts.map +1 -0
  46. package/dist/normalize.js +20 -0
  47. package/dist/normalize.js.map +1 -0
  48. package/dist/pretty-widget.d.ts +6 -4
  49. package/dist/pretty-widget.d.ts.map +1 -1
  50. package/dist/pretty-widget.js +64 -24
  51. package/dist/pretty-widget.js.map +1 -1
  52. package/dist/queries.d.ts +129 -90
  53. package/dist/queries.d.ts.map +1 -1
  54. package/dist/queries.js +235 -164
  55. package/dist/queries.js.map +1 -1
  56. package/dist/register-test-hooks.js +2 -1
  57. package/dist/register-test-hooks.js.map +1 -1
  58. package/dist/render-hook.d.ts +3 -2
  59. package/dist/render-hook.d.ts.map +1 -1
  60. package/dist/render-hook.js +2 -1
  61. package/dist/render-hook.js.map +1 -1
  62. package/dist/render.d.ts +3 -2
  63. package/dist/render.d.ts.map +1 -1
  64. package/dist/render.js +25 -19
  65. package/dist/render.js.map +1 -1
  66. package/dist/role-helpers.d.ts +6 -5
  67. package/dist/role-helpers.d.ts.map +1 -1
  68. package/dist/role-helpers.js +16 -12
  69. package/dist/role-helpers.js.map +1 -1
  70. package/dist/screen.d.ts +4 -3
  71. package/dist/screen.d.ts.map +1 -1
  72. package/dist/screen.js +13 -11
  73. package/dist/screen.js.map +1 -1
  74. package/dist/screenshot.d.ts +4 -3
  75. package/dist/screenshot.d.ts.map +1 -1
  76. package/dist/screenshot.js +43 -36
  77. package/dist/screenshot.js.map +1 -1
  78. package/dist/suggestions.d.ts +11 -6
  79. package/dist/suggestions.d.ts.map +1 -1
  80. package/dist/suggestions.js +10 -5
  81. package/dist/suggestions.js.map +1 -1
  82. package/dist/traversal.d.ts +7 -7
  83. package/dist/traversal.d.ts.map +1 -1
  84. package/dist/traversal.js +17 -11
  85. package/dist/traversal.js.map +1 -1
  86. package/dist/types.d.ts +65 -24
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js.map +1 -1
  89. package/dist/user-event/adjustment.d.ts +5 -3
  90. package/dist/user-event/adjustment.d.ts.map +1 -1
  91. package/dist/user-event/adjustment.js +6 -4
  92. package/dist/user-event/adjustment.js.map +1 -1
  93. package/dist/user-event/click.d.ts +6 -5
  94. package/dist/user-event/click.d.ts.map +1 -1
  95. package/dist/user-event/click.js +29 -21
  96. package/dist/user-event/click.js.map +1 -1
  97. package/dist/user-event/controller.d.ts +7 -5
  98. package/dist/user-event/controller.d.ts.map +1 -1
  99. package/dist/user-event/controller.js +16 -6
  100. package/dist/user-event/controller.js.map +1 -1
  101. package/dist/user-event/dispatch.d.ts +3 -3
  102. package/dist/user-event/dispatch.d.ts.map +1 -1
  103. package/dist/user-event/dispatch.js +3 -2
  104. package/dist/user-event/dispatch.js.map +1 -1
  105. package/dist/user-event/event-wrapper.d.ts +2 -1
  106. package/dist/user-event/event-wrapper.d.ts.map +1 -1
  107. package/dist/user-event/event-wrapper.js +27 -16
  108. package/dist/user-event/event-wrapper.js.map +1 -1
  109. package/dist/user-event/gesture.d.ts +18 -14
  110. package/dist/user-event/gesture.d.ts.map +1 -1
  111. package/dist/user-event/gesture.js +70 -37
  112. package/dist/user-event/gesture.js.map +1 -1
  113. package/dist/user-event/index.d.ts +17 -10
  114. package/dist/user-event/index.d.ts.map +1 -1
  115. package/dist/user-event/index.js +7 -3
  116. package/dist/user-event/index.js.map +1 -1
  117. package/dist/user-event/keyboard.d.ts +4 -3
  118. package/dist/user-event/keyboard.d.ts.map +1 -1
  119. package/dist/user-event/keyboard.js +89 -58
  120. package/dist/user-event/keyboard.js.map +1 -1
  121. package/dist/user-event/pointer.d.ts +7 -3
  122. package/dist/user-event/pointer.d.ts.map +1 -1
  123. package/dist/user-event/pointer.js +4 -1
  124. package/dist/user-event/pointer.js.map +1 -1
  125. package/dist/user-event/selection.d.ts +3 -2
  126. package/dist/user-event/selection.d.ts.map +1 -1
  127. package/dist/user-event/selection.js +26 -27
  128. package/dist/user-event/selection.js.map +1 -1
  129. package/dist/user-event/state.d.ts +3 -2
  130. package/dist/user-event/state.d.ts.map +1 -1
  131. package/dist/user-event/state.js +2 -1
  132. package/dist/user-event/state.js.map +1 -1
  133. package/dist/user-event/text.d.ts +8 -7
  134. package/dist/user-event/text.d.ts.map +1 -1
  135. package/dist/user-event/text.js +39 -33
  136. package/dist/user-event/text.js.map +1 -1
  137. package/dist/wait-for.d.ts +5 -5
  138. package/dist/wait-for.d.ts.map +1 -1
  139. package/dist/wait-for.js +45 -30
  140. package/dist/wait-for.js.map +1 -1
  141. package/dist/widget-accessible-properties.d.ts +36 -25
  142. package/dist/widget-accessible-properties.d.ts.map +1 -1
  143. package/dist/widget-accessible-properties.js +215 -68
  144. package/dist/widget-accessible-properties.js.map +1 -1
  145. package/dist/within.d.ts +2 -1
  146. package/dist/within.d.ts.map +1 -1
  147. package/dist/within.js +2 -1
  148. package/dist/within.js.map +1 -1
  149. package/package.json +11 -9
  150. package/src/act.ts +44 -35
  151. package/src/bound-queries.ts +6 -6
  152. package/src/build-queries.ts +84 -43
  153. package/src/cleanup-registry.ts +18 -5
  154. package/src/config.ts +16 -12
  155. package/src/editable.ts +13 -6
  156. package/src/errors.ts +106 -45
  157. package/src/fire-event.ts +3 -1
  158. package/src/frame-sync.ts +38 -21
  159. package/src/index.ts +13 -3
  160. package/src/matchers.ts +568 -114
  161. package/src/normalize.ts +28 -0
  162. package/src/pretty-widget.ts +114 -42
  163. package/src/queries.ts +418 -256
  164. package/src/register-test-hooks.ts +4 -1
  165. package/src/render-hook.tsx +10 -4
  166. package/src/render.tsx +59 -34
  167. package/src/role-helpers.ts +29 -16
  168. package/src/screen.ts +17 -12
  169. package/src/screenshot.ts +61 -38
  170. package/src/suggestions.ts +29 -10
  171. package/src/traversal.ts +35 -15
  172. package/src/types.ts +111 -32
  173. package/src/user-event/adjustment.ts +23 -10
  174. package/src/user-event/click.ts +49 -20
  175. package/src/user-event/controller.ts +41 -7
  176. package/src/user-event/dispatch.ts +4 -2
  177. package/src/user-event/event-wrapper.ts +42 -16
  178. package/src/user-event/gesture.ts +135 -50
  179. package/src/user-event/index.ts +19 -12
  180. package/src/user-event/keyboard.ts +147 -61
  181. package/src/user-event/pointer.ts +20 -8
  182. package/src/user-event/selection.ts +69 -32
  183. package/src/user-event/state.ts +4 -2
  184. package/src/user-event/text.ts +73 -38
  185. package/src/wait-for.ts +77 -36
  186. package/src/widget-accessible-properties.ts +385 -79
  187. 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") return globalThis;
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
- export const getIsReactActEnvironment = (): boolean | undefined => getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
28
+ const getIsReactActEnvironment = (): boolean | undefined => getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
13
29
 
14
- export const setIsReactActEnvironment = (value: boolean | undefined): void => {
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
- export const runWithActEnvironment = <T>(forced: boolean, fn: () => T | PromiseLike<T>): T | PromiseLike<T> => {
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(forced);
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 Promise.resolve(result).then(
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
- return result;
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
- const withGlobalActEnvironment =
50
- (actImplementation: ActImplementation) =>
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 actImplementation: ActImplementation = reactAct as ActImplementation;
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 const runInAct = (callback: () => unknown): Promise<void> =>
68
- Promise.resolve(act(() => callback())).then(() => undefined);
77
+ export { act, getIsReactActEnvironment, setIsReactActEnvironment, runWithActEnvironment, runInAct };
@@ -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
- export type BoundQueries = BoundCustomQueries<typeof builtinQueries>;
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
- export type Screen = BoundQueries & DebugUtilities;
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
- export type RenderResult<Q extends QueryMap = Record<never, never>> = BoundQueries &
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 };
@@ -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
- export type QueryAllBy<Args extends unknown[]> = (container: Container, ...args: Args) => Gtk.Widget[];
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
- export type BuiltQueries<Args extends unknown[]> = {
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[args.length - 1];
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[args.length - 1];
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
- if (!shouldSuggest) return;
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) return 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
- (container, ...args) => {
100
- const matches = allQuery(container, ...args);
101
- if (matches.length > 1) throw getMultipleError(container, matches, ...args);
102
- return matches[0] ?? null;
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
- (container, ...args) => {
111
- const matches = allQuery(container, ...args);
112
- if (matches.length === 0) throw getMissingError(container, ...args);
113
- return matches;
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
- (container, ...args) => {
119
- const match = query(container, ...args);
120
- if (match) {
121
- maybeThrowSuggestion({ container, match, queryName, variant, suggest: extractSuggestOption(args) });
122
- }
123
- return match;
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
- (container, ...args) => {
129
- const matches = query(container, ...args);
130
- const [first] = matches;
131
- if (first !== undefined) {
132
- maybeThrowSuggestion({ container, match: first, queryName, variant, suggest: extractSuggestOption(args) });
133
- }
134
- return matches;
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
- (container: Container, ...args: Args): Gtk.Widget => {
140
- const match = query(container, ...args);
141
- if (match === null) throw getMissingError(container, ...args);
142
- return match;
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
- export const buildQueries = <Args extends unknown[]>(
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 };
@@ -1,14 +1,27 @@
1
- export type CleanupFunction = () => void | Promise<void>;
1
+ type CleanupFunction = () => void | Promise<void>;
2
2
 
3
- const cleanupQueue = new Set<CleanupFunction>();
3
+ const cleanupQueue: Set<CleanupFunction> = new Set();
4
4
 
5
- export const addToCleanupQueue = (fn: CleanupFunction): void => {
5
+ const addToCleanupQueue = (fn: CleanupFunction): void => {
6
6
  cleanupQueue.add(fn);
7
7
  };
8
8
 
9
- export const runCleanup = async (): Promise<void> => {
9
+ const runCleanup = async (): Promise<void> => {
10
+ const failures: unknown[] = [];
11
+
10
12
  for (const fn of cleanupQueue) {
11
- await fn();
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
- export type Config = {
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
- export type ConfigFn = (existingConfig: Config) => Partial<Config>;
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
- let currentConfig: Config = { ...defaultConfig };
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
- export const getConfig = (): Config => {
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
- export const configure = (newConfig: Partial<Config> | ConfigFn): void => {
53
+ const configure = (newConfig: Partial<Config> | ConfigFn): void => {
56
54
  const updates = typeof newConfig === "function" ? newConfig(currentConfig) : newConfig;
57
- currentConfig = { ...currentConfig, ...updates };
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
- export type EditableTarget = Gtk.Editable | Gtk.TextView;
3
+ type EditableTarget = Gtk.Editable | Gtk.TextView;
4
4
 
5
- export const EDITABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
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
- export const isEditable = (widget: unknown): widget is EditableTarget =>
11
+ const isEditable = (widget: unknown): widget is EditableTarget =>
12
12
  widget instanceof Gtk.Editable || widget instanceof Gtk.TextView;
13
13
 
14
- export const getEditableDelegate = (widget: Gtk.Widget): Gtk.Widget | null => {
15
- if (!(widget instanceof Gtk.Editable)) return null;
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
- export const readEditableText = (widget: EditableTarget): string => {
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 };