@gtkx/testing 0.1.33 → 0.1.35

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/README.md CHANGED
@@ -119,10 +119,12 @@ import { AccessibleRole } from "@gtkx/ffi/gtk";
119
119
  import { App } from "./app.js";
120
120
 
121
121
  // Clean up after each test
122
- afterEach(() => cleanup());
122
+ afterEach(async () => {
123
+ await cleanup();
124
+ });
123
125
 
124
126
  test("increments count when clicking button", async () => {
125
- render(<App />);
127
+ await render(<App />);
126
128
 
127
129
  const button = await screen.findByRole(AccessibleRole.BUTTON, {
128
130
  name: "Increment",
@@ -132,8 +134,8 @@ test("increments count when clicking button", async () => {
132
134
  await screen.findByText("Count: 1");
133
135
  });
134
136
 
135
- test("can also use fireEvent for synchronous events", async () => {
136
- render(<App />);
137
+ test("can also use fireEvent for low-level events", async () => {
138
+ await render(<App />);
137
139
 
138
140
  const button = await screen.findByRole(AccessibleRole.BUTTON, {
139
141
  name: "Increment",
@@ -146,10 +148,8 @@ test("can also use fireEvent for synchronous events", async () => {
146
148
 
147
149
  ### Available APIs
148
150
 
149
- **Queries** - Find elements in the rendered tree:
150
- - `getBy*` / `getAllBy*` - Throws if not found
151
- - `queryBy*` / `queryAllBy*` - Returns null/empty array if not found
152
- - `findBy*` / `findAllBy*` - Async, waits for element
151
+ **Queries** - Find elements in the rendered tree (all async):
152
+ - `findBy*` / `findAllBy*` - Waits for element to appear
153
153
 
154
154
  Query types: `ByRole`, `ByText`, `ByLabelText`, `ByTestId`
155
155
 
@@ -1,22 +1,45 @@
1
1
  import { call } from "@gtkx/native";
2
- import { getWidgetPtr } from "./widget.js";
2
+ const hasToggle = (widget) => "getActive" in widget &&
3
+ typeof widget.getActive === "function" &&
4
+ "setActive" in widget &&
5
+ typeof widget.setActive === "function";
3
6
  const emitSignal = (widget, signalName) => {
4
7
  call("libgobject-2.0.so.0", "g_signal_emit_by_name", [
5
- { type: { type: "gobject" }, value: getWidgetPtr(widget) },
8
+ { type: { type: "gobject" }, value: widget.ptr },
6
9
  { type: { type: "string" }, value: signalName },
7
10
  ], { type: "undefined" });
8
11
  };
9
12
  export const fireEvent = Object.assign((element, signalName) => {
10
- emitSignal(element, signalName);
13
+ switch (signalName) {
14
+ case "clicked":
15
+ emitSignal(element, "clicked");
16
+ break;
17
+ case "activate":
18
+ element.activate();
19
+ break;
20
+ case "toggled":
21
+ if (hasToggle(element)) {
22
+ element.setActive(!element.getActive());
23
+ }
24
+ break;
25
+ case "changed":
26
+ emitSignal(element, "changed");
27
+ break;
28
+ default:
29
+ throw new Error(`fireEvent: Signal "${signalName}" is not supported. ` +
30
+ `Supported signals: clicked, activate, toggled, changed`);
31
+ }
11
32
  }, {
12
33
  click: (element) => {
13
34
  emitSignal(element, "clicked");
14
35
  },
15
36
  activate: (element) => {
16
- emitSignal(element, "activate");
37
+ element.activate();
17
38
  },
18
39
  toggled: (element) => {
19
- emitSignal(element, "toggled");
40
+ if (hasToggle(element)) {
41
+ element.setActive(!element.getActive());
42
+ }
20
43
  },
21
44
  changed: (element) => {
22
45
  emitSignal(element, "changed");
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { fireEvent } from "./fire-event.js";
2
- export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, getAllByLabelText, getAllByRole, getAllByTestId, getAllByText, getByLabelText, getByRole, getByTestId, getByText, queryAllByLabelText, queryAllByRole, queryAllByTestId, queryAllByText, queryByLabelText, queryByRole, queryByTestId, queryByText, } from "./queries.js";
2
+ export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, } from "./queries.js";
3
3
  export { cleanup, render, teardown } from "./render.js";
4
4
  export { screen } from "./screen.js";
5
5
  export type { ByRoleOptions, RenderOptions, RenderResult, TextMatchOptions, WaitForOptions, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { fireEvent } from "./fire-event.js";
2
- export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, getAllByLabelText, getAllByRole, getAllByTestId, getAllByText, getByLabelText, getByRole, getByTestId, getByText, queryAllByLabelText, queryAllByRole, queryAllByTestId, queryAllByText, queryByLabelText, queryByRole, queryByTestId, queryByText, } from "./queries.js";
2
+ export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, } from "./queries.js";
3
3
  export { cleanup, render, teardown } from "./render.js";
4
4
  export { screen } from "./screen.js";
5
5
  export { userEvent } from "./user-event.js";
package/dist/queries.d.ts CHANGED
@@ -2,22 +2,6 @@ import type * as Gtk from "@gtkx/ffi/gtk";
2
2
  import { AccessibleRole } from "@gtkx/ffi/gtk";
3
3
  import type { ByRoleOptions, TextMatchOptions } from "./types.js";
4
4
  type Container = Gtk.Application | Gtk.Widget;
5
- export declare const getAllByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
6
- export declare const getByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget;
7
- export declare const queryAllByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
8
- export declare const queryByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget | null;
9
- export declare const getAllByLabelText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
10
- export declare const getByLabelText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
11
- export declare const queryAllByLabelText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
12
- export declare const queryByLabelText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
13
- export declare const getAllByText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
14
- export declare const getByText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
15
- export declare const queryAllByText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
16
- export declare const queryByText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
17
- export declare const getAllByTestId: (container: Container, testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
18
- export declare const getByTestId: (container: Container, testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
19
- export declare const queryAllByTestId: (container: Container, testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
20
- export declare const queryByTestId: (container: Container, testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
21
5
  export declare const findByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget>;
22
6
  export declare const findAllByRole: (container: Container, role: AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget[]>;
23
7
  export declare const findByLabelText: (container: Container, text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget>;
package/dist/queries.js CHANGED
@@ -1,8 +1,7 @@
1
- import { AccessibleRole } from "@gtkx/ffi/gtk";
2
- import { call } from "@gtkx/native";
1
+ import { wrapPtr } from "@gtkx/ffi";
2
+ import { AccessibleRole, Button, CheckButton, Expander, Label, ToggleButton } from "@gtkx/ffi/gtk";
3
3
  import { findAll } from "./traversal.js";
4
4
  import { waitFor } from "./wait-for.js";
5
- import { getWidgetPtr } from "./widget.js";
6
5
  const DEFAULT_NORMALIZER = (text) => text.trim().replace(/\s+/g, " ");
7
6
  const normalizeText = (text, options) => {
8
7
  const normalizer = options?.normalizer ?? DEFAULT_NORMALIZER;
@@ -19,13 +18,11 @@ const matchText = (actual, expected, options) => {
19
18
  }
20
19
  return expected.test(normalizedActual);
21
20
  };
22
- const callGetter = (ptr, funcName) => {
23
- const result = call("libgtk-4.so.1", funcName, [{ type: { type: "gobject" }, value: ptr }], {
24
- type: "string",
25
- borrowed: true,
26
- });
27
- return result;
28
- };
21
+ const asButton = (widget) => wrapPtr(widget.ptr, Button);
22
+ const asLabel = (widget) => wrapPtr(widget.ptr, Label);
23
+ const asCheckButton = (widget) => wrapPtr(widget.ptr, CheckButton);
24
+ const asToggleButton = (widget) => wrapPtr(widget.ptr, ToggleButton);
25
+ const asExpander = (widget) => wrapPtr(widget.ptr, Expander);
29
26
  const isInternalLabel = (widget) => {
30
27
  const accessible = widget;
31
28
  if (accessible.getAccessibleRole() !== AccessibleRole.LABEL)
@@ -44,9 +41,6 @@ const isInternalLabel = (widget) => {
44
41
  parentRole === AccessibleRole.MENU_ITEM_RADIO);
45
42
  };
46
43
  const getWidgetText = (widget) => {
47
- const ptr = getWidgetPtr(widget);
48
- if (!ptr)
49
- return null;
50
44
  if (isInternalLabel(widget))
51
45
  return null;
52
46
  const accessible = widget;
@@ -59,57 +53,39 @@ const getWidgetText = (widget) => {
59
53
  case AccessibleRole.MENU_ITEM:
60
54
  case AccessibleRole.MENU_ITEM_CHECKBOX:
61
55
  case AccessibleRole.MENU_ITEM_RADIO:
62
- return callGetter(ptr, "gtk_button_get_label");
56
+ return asButton(widget).getLabel();
63
57
  case AccessibleRole.LABEL:
64
- return callGetter(ptr, "gtk_label_get_label");
58
+ return asLabel(widget).getLabel();
65
59
  case AccessibleRole.TEXT_BOX:
66
60
  case AccessibleRole.SEARCH_BOX:
67
61
  case AccessibleRole.SPIN_BUTTON:
68
- return callGetter(ptr, "gtk_editable_get_text");
62
+ return widget.getText();
69
63
  default:
70
64
  return null;
71
65
  }
72
66
  };
73
67
  const getWidgetTestId = (widget) => {
74
- const ptr = getWidgetPtr(widget);
75
- if (!ptr)
76
- return null;
77
- const result = call("libgtk-4.so.1", "gtk_widget_get_name", [{ type: { type: "gobject" }, value: ptr }], {
78
- type: "string",
79
- borrowed: true,
80
- });
81
- return result;
68
+ return widget.getName();
82
69
  };
83
70
  const getWidgetCheckedState = (widget) => {
84
- const ptr = getWidgetPtr(widget);
85
- if (!ptr)
86
- return undefined;
87
71
  const accessible = widget;
88
72
  const role = accessible.getAccessibleRole();
89
73
  if (role === AccessibleRole.CHECKBOX || role === AccessibleRole.RADIO) {
90
- const result = call("libgtk-4.so.1", "gtk_check_button_get_active", [{ type: { type: "gobject" }, value: ptr }], {
91
- type: "boolean",
92
- });
93
- return result === true;
74
+ return asCheckButton(widget).getActive();
94
75
  }
95
76
  if (role === AccessibleRole.TOGGLE_BUTTON) {
96
- const result = call("libgtk-4.so.1", "gtk_toggle_button_get_active", [{ type: { type: "gobject" }, value: ptr }], { type: "boolean" });
97
- return result === true;
77
+ return asToggleButton(widget).getActive();
98
78
  }
99
79
  return undefined;
100
80
  };
101
81
  const getWidgetExpandedState = (widget) => {
102
- const ptr = getWidgetPtr(widget);
103
- if (!ptr)
104
- return undefined;
105
82
  const accessible = widget;
106
83
  const role = accessible.getAccessibleRole();
107
84
  if (role === AccessibleRole.BUTTON) {
108
- const expanderPtr = call("libgtk-4.so.1", "gtk_widget_get_parent", [{ type: { type: "gobject" }, value: ptr }], { type: "gobject" });
109
- if (!expanderPtr)
85
+ const parent = widget.getParent();
86
+ if (!parent)
110
87
  return undefined;
111
- const result = call("libgtk-4.so.1", "gtk_expander_get_expanded", [{ type: { type: "gobject" }, value: expanderPtr }], { type: "boolean" });
112
- return result === true;
88
+ return asExpander(parent).getExpanded();
113
89
  }
114
90
  return undefined;
115
91
  };
@@ -150,7 +126,7 @@ const formatByRoleError = (role, options) => {
150
126
  parts.push(`level=${options.level}`);
151
127
  return parts.join(" and ");
152
128
  };
153
- export const getAllByRole = (container, role, options) => {
129
+ const getAllByRole = (container, role, options) => {
154
130
  const matches = findAll(container, (node) => {
155
131
  const accessible = node;
156
132
  if (accessible.getAccessibleRole() !== role)
@@ -162,29 +138,14 @@ export const getAllByRole = (container, role, options) => {
162
138
  }
163
139
  return matches;
164
140
  };
165
- export const getByRole = (container, role, options) => {
141
+ const getByRole = (container, role, options) => {
166
142
  const matches = getAllByRole(container, role, options);
167
143
  if (matches.length > 1) {
168
144
  throw new Error(`Found ${matches.length} elements with ${formatByRoleError(role, options)}`);
169
145
  }
170
146
  return matches[0];
171
147
  };
172
- export const queryAllByRole = (container, role, options) => {
173
- return findAll(container, (node) => {
174
- const accessible = node;
175
- if (accessible.getAccessibleRole() !== role)
176
- return false;
177
- return matchByRoleOptions(node, options);
178
- });
179
- };
180
- export const queryByRole = (container, role, options) => {
181
- const matches = queryAllByRole(container, role, options);
182
- if (matches.length > 1) {
183
- throw new Error(`Found ${matches.length} elements with ${formatByRoleError(role, options)}`);
184
- }
185
- return matches[0] ?? null;
186
- };
187
- export const getAllByLabelText = (container, text, options) => {
148
+ const getAllByLabelText = (container, text, options) => {
188
149
  const matches = findAll(container, (node) => {
189
150
  const widgetText = getWidgetText(node);
190
151
  return matchText(widgetText, text, options);
@@ -194,27 +155,14 @@ export const getAllByLabelText = (container, text, options) => {
194
155
  }
195
156
  return matches;
196
157
  };
197
- export const getByLabelText = (container, text, options) => {
158
+ const getByLabelText = (container, text, options) => {
198
159
  const matches = getAllByLabelText(container, text, options);
199
160
  if (matches.length > 1) {
200
161
  throw new Error(`Found ${matches.length} elements with label text "${text}"`);
201
162
  }
202
163
  return matches[0];
203
164
  };
204
- export const queryAllByLabelText = (container, text, options) => {
205
- return findAll(container, (node) => {
206
- const widgetText = getWidgetText(node);
207
- return matchText(widgetText, text, options);
208
- });
209
- };
210
- export const queryByLabelText = (container, text, options) => {
211
- const matches = queryAllByLabelText(container, text, options);
212
- if (matches.length > 1) {
213
- throw new Error(`Found ${matches.length} elements with label text "${text}"`);
214
- }
215
- return matches[0] ?? null;
216
- };
217
- export const getAllByText = (container, text, options) => {
165
+ const getAllByText = (container, text, options) => {
218
166
  const matches = findAll(container, (node) => {
219
167
  const widgetText = getWidgetText(node);
220
168
  return matchText(widgetText, text, options);
@@ -224,27 +172,14 @@ export const getAllByText = (container, text, options) => {
224
172
  }
225
173
  return matches;
226
174
  };
227
- export const getByText = (container, text, options) => {
175
+ const getByText = (container, text, options) => {
228
176
  const matches = getAllByText(container, text, options);
229
177
  if (matches.length > 1) {
230
178
  throw new Error(`Found ${matches.length} elements with text "${text}"`);
231
179
  }
232
180
  return matches[0];
233
181
  };
234
- export const queryAllByText = (container, text, options) => {
235
- return findAll(container, (node) => {
236
- const widgetText = getWidgetText(node);
237
- return matchText(widgetText, text, options);
238
- });
239
- };
240
- export const queryByText = (container, text, options) => {
241
- const matches = queryAllByText(container, text, options);
242
- if (matches.length > 1) {
243
- throw new Error(`Found ${matches.length} elements with text "${text}"`);
244
- }
245
- return matches[0] ?? null;
246
- };
247
- export const getAllByTestId = (container, testId, options) => {
182
+ const getAllByTestId = (container, testId, options) => {
248
183
  const matches = findAll(container, (node) => {
249
184
  const widgetTestId = getWidgetTestId(node);
250
185
  return matchText(widgetTestId, testId, options);
@@ -254,26 +189,13 @@ export const getAllByTestId = (container, testId, options) => {
254
189
  }
255
190
  return matches;
256
191
  };
257
- export const getByTestId = (container, testId, options) => {
192
+ const getByTestId = (container, testId, options) => {
258
193
  const matches = getAllByTestId(container, testId, options);
259
194
  if (matches.length > 1) {
260
195
  throw new Error(`Found ${matches.length} elements with test id "${testId}"`);
261
196
  }
262
197
  return matches[0];
263
198
  };
264
- export const queryAllByTestId = (container, testId, options) => {
265
- return findAll(container, (node) => {
266
- const widgetTestId = getWidgetTestId(node);
267
- return matchText(widgetTestId, testId, options);
268
- });
269
- };
270
- export const queryByTestId = (container, testId, options) => {
271
- const matches = queryAllByTestId(container, testId, options);
272
- if (matches.length > 1) {
273
- throw new Error(`Found ${matches.length} elements with test id "${testId}"`);
274
- }
275
- return matches[0] ?? null;
276
- };
277
199
  export const findByRole = async (container, role, options) => waitFor(() => getByRole(container, role, options));
278
200
  export const findAllByRole = async (container, role, options) => waitFor(() => getAllByRole(container, role, options));
279
201
  export const findByLabelText = async (container, text, options) => waitFor(() => getByLabelText(container, text, options));
package/dist/render.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { RenderOptions, RenderResult } from "./types.js";
3
- export declare const render: (element: ReactNode, options?: RenderOptions) => RenderResult;
4
- export declare const cleanup: () => void;
5
- export declare const teardown: () => void;
3
+ export declare const render: (element: ReactNode, options?: RenderOptions) => Promise<RenderResult>;
4
+ export declare const cleanup: () => Promise<void>;
5
+ export declare const teardown: () => Promise<void>;
package/dist/render.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { start, stop } from "@gtkx/ffi";
2
+ import { getCurrentApp, start, stop } from "@gtkx/ffi";
3
3
  import * as Gtk from "@gtkx/ffi/gtk";
4
4
  import { ApplicationWindow, reconciler } from "@gtkx/react";
5
5
  import * as queries from "./queries.js";
6
6
  import { setScreenRoot } from "./screen.js";
7
7
  const APP_ID = "com.gtkx.testing";
8
- let app = null;
9
8
  let container = null;
10
9
  const hasGetLabel = (widget) => typeof widget.getLabel === "function";
11
10
  const printWidgetTree = (root, indent = 0) => {
@@ -21,36 +20,13 @@ const printWidgetTree = (root, indent = 0) => {
21
20
  }
22
21
  return result;
23
22
  };
24
- const updateSync = (instance, element, fiberRoot) => {
25
- const instanceAny = instance;
26
- if (typeof instanceAny.flushSync === "function") {
27
- instanceAny.flushSync(() => {
28
- instance.updateContainer(element, fiberRoot, null, () => { });
29
- });
30
- }
31
- else {
32
- if (typeof instanceAny.updateContainerSync === "function") {
33
- instanceAny.updateContainerSync(element, fiberRoot, null, () => { });
34
- }
35
- else {
36
- instance.updateContainer(element, fiberRoot, null, () => { });
37
- }
38
- if (typeof instanceAny.flushSyncWork === "function") {
39
- instanceAny.flushSyncWork();
40
- }
41
- }
42
- instance.flushPassiveEffects();
23
+ const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
24
+ const update = async (instance, element, fiberRoot) => {
25
+ instance.updateContainer(element, fiberRoot, null, () => { });
26
+ await tick();
43
27
  };
44
28
  const ensureInitialized = () => {
45
- if (!app) {
46
- try {
47
- app = reconciler.getApp();
48
- }
49
- catch {
50
- app = start(APP_ID);
51
- reconciler.setApp(app);
52
- }
53
- }
29
+ const app = start(APP_ID);
54
30
  if (!container) {
55
31
  const instance = reconciler.getInstance();
56
32
  container = instance.createContainer(app, 0, null, false, null, "", (error) => console.error("Test reconciler error:", error), () => { }, () => { }, () => { }, null);
@@ -59,30 +35,14 @@ const ensureInitialized = () => {
59
35
  };
60
36
  const DefaultWrapper = ({ children }) => (_jsx(ApplicationWindow, { children: children }));
61
37
  const wrapElement = (element, Wrapper = DefaultWrapper) => (_jsx(Wrapper, { children: element }));
62
- export const render = (element, options) => {
38
+ export const render = async (element, options) => {
63
39
  const { app: application, container: fiberRoot } = ensureInitialized();
64
40
  const instance = reconciler.getInstance();
65
41
  const wrappedElement = wrapElement(element, options?.wrapper);
66
- updateSync(instance, wrappedElement, fiberRoot);
42
+ await update(instance, wrappedElement, fiberRoot);
67
43
  setScreenRoot(application);
68
44
  return {
69
45
  container: application,
70
- getByRole: (role, opts) => queries.getByRole(application, role, opts),
71
- getByLabelText: (text, opts) => queries.getByLabelText(application, text, opts),
72
- getByText: (text, opts) => queries.getByText(application, text, opts),
73
- getByTestId: (testId, opts) => queries.getByTestId(application, testId, opts),
74
- queryByRole: (role, opts) => queries.queryByRole(application, role, opts),
75
- queryByLabelText: (text, opts) => queries.queryByLabelText(application, text, opts),
76
- queryByText: (text, opts) => queries.queryByText(application, text, opts),
77
- queryByTestId: (testId, opts) => queries.queryByTestId(application, testId, opts),
78
- getAllByRole: (role, opts) => queries.getAllByRole(application, role, opts),
79
- getAllByLabelText: (text, opts) => queries.getAllByLabelText(application, text, opts),
80
- getAllByText: (text, opts) => queries.getAllByText(application, text, opts),
81
- getAllByTestId: (testId, opts) => queries.getAllByTestId(application, testId, opts),
82
- queryAllByRole: (role, opts) => queries.queryAllByRole(application, role, opts),
83
- queryAllByLabelText: (text, opts) => queries.queryAllByLabelText(application, text, opts),
84
- queryAllByText: (text, opts) => queries.queryAllByText(application, text, opts),
85
- queryAllByTestId: (testId, opts) => queries.queryAllByTestId(application, testId, opts),
86
46
  findByRole: (role, opts) => queries.findByRole(application, role, opts),
87
47
  findByLabelText: (text, opts) => queries.findByLabelText(application, text, opts),
88
48
  findByText: (text, opts) => queries.findByText(application, text, opts),
@@ -91,10 +51,10 @@ export const render = (element, options) => {
91
51
  findAllByLabelText: (text, opts) => queries.findAllByLabelText(application, text, opts),
92
52
  findAllByText: (text, opts) => queries.findAllByText(application, text, opts),
93
53
  findAllByTestId: (testId, opts) => queries.findAllByTestId(application, testId, opts),
94
- unmount: () => updateSync(instance, null, fiberRoot),
54
+ unmount: () => update(instance, null, fiberRoot),
95
55
  rerender: (newElement) => {
96
56
  const wrapped = wrapElement(newElement, options?.wrapper);
97
- updateSync(instance, wrapped, fiberRoot);
57
+ return update(instance, wrapped, fiberRoot);
98
58
  },
99
59
  debug: () => {
100
60
  const activeWindow = application.getActiveWindow();
@@ -104,10 +64,11 @@ export const render = (element, options) => {
104
64
  },
105
65
  };
106
66
  };
107
- export const cleanup = () => {
108
- if (container && app) {
67
+ export const cleanup = async () => {
68
+ if (container) {
69
+ const app = getCurrentApp();
109
70
  const instance = reconciler.getInstance();
110
- updateSync(instance, null, container);
71
+ await update(instance, null, container);
111
72
  for (const window of app.getWindows()) {
112
73
  window.destroy();
113
74
  }
@@ -115,11 +76,8 @@ export const cleanup = () => {
115
76
  container = null;
116
77
  setScreenRoot(null);
117
78
  };
118
- export const teardown = () => {
119
- if (app) {
120
- cleanup();
121
- stop();
122
- app = null;
123
- container = null;
124
- }
79
+ export const teardown = async () => {
80
+ await cleanup();
81
+ stop();
82
+ container = null;
125
83
  };
package/dist/screen.d.ts CHANGED
@@ -3,22 +3,6 @@ import type { AccessibleRole } from "@gtkx/ffi/gtk";
3
3
  import type { ByRoleOptions, TextMatchOptions } from "./types.js";
4
4
  export declare const setScreenRoot: (root: Gtk.Application | null) => void;
5
5
  export declare const screen: {
6
- getByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget;
7
- getByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
8
- getByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
9
- getByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
10
- queryByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget | null;
11
- queryByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
12
- queryByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
13
- queryByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
14
- getAllByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
15
- getAllByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
16
- getAllByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
17
- getAllByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
18
- queryAllByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
19
- queryAllByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
20
- queryAllByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
21
- queryAllByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
22
6
  findByRole: (role: AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget>;
23
7
  findByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget>;
24
8
  findByText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget>;
package/dist/screen.js CHANGED
@@ -10,22 +10,6 @@ const getRoot = () => {
10
10
  return currentRoot;
11
11
  };
12
12
  export const screen = {
13
- getByRole: (role, options) => queries.getByRole(getRoot(), role, options),
14
- getByLabelText: (text, options) => queries.getByLabelText(getRoot(), text, options),
15
- getByText: (text, options) => queries.getByText(getRoot(), text, options),
16
- getByTestId: (testId, options) => queries.getByTestId(getRoot(), testId, options),
17
- queryByRole: (role, options) => queries.queryByRole(getRoot(), role, options),
18
- queryByLabelText: (text, options) => queries.queryByLabelText(getRoot(), text, options),
19
- queryByText: (text, options) => queries.queryByText(getRoot(), text, options),
20
- queryByTestId: (testId, options) => queries.queryByTestId(getRoot(), testId, options),
21
- getAllByRole: (role, options) => queries.getAllByRole(getRoot(), role, options),
22
- getAllByLabelText: (text, options) => queries.getAllByLabelText(getRoot(), text, options),
23
- getAllByText: (text, options) => queries.getAllByText(getRoot(), text, options),
24
- getAllByTestId: (testId, options) => queries.getAllByTestId(getRoot(), testId, options),
25
- queryAllByRole: (role, options) => queries.queryAllByRole(getRoot(), role, options),
26
- queryAllByLabelText: (text, options) => queries.queryAllByLabelText(getRoot(), text, options),
27
- queryAllByText: (text, options) => queries.queryAllByText(getRoot(), text, options),
28
- queryAllByTestId: (testId, options) => queries.queryAllByTestId(getRoot(), testId, options),
29
13
  findByRole: (role, options) => queries.findByRole(getRoot(), role, options),
30
14
  findByLabelText: (text, options) => queries.findByLabelText(getRoot(), text, options),
31
15
  findByText: (text, options) => queries.findByText(getRoot(), text, options),
package/dist/types.d.ts CHANGED
@@ -25,22 +25,6 @@ export interface RenderOptions {
25
25
  }
26
26
  export interface RenderResult {
27
27
  container: Gtk.Application;
28
- getByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget;
29
- getByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
30
- getByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
31
- getByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget;
32
- queryByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget | null;
33
- queryByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
34
- queryByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
35
- queryByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget | null;
36
- getAllByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
37
- getAllByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
38
- getAllByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
39
- getAllByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
40
- queryAllByRole: (role: AccessibleRole, options?: ByRoleOptions) => Gtk.Widget[];
41
- queryAllByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
42
- queryAllByText: (text: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
43
- queryAllByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Gtk.Widget[];
44
28
  findByRole: (role: AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget>;
45
29
  findByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget>;
46
30
  findByText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget>;
@@ -49,7 +33,7 @@ export interface RenderResult {
49
33
  findAllByLabelText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
50
34
  findAllByText: (text: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
51
35
  findAllByTestId: (testId: string | RegExp, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
52
- unmount: () => void;
53
- rerender: (element: ReactNode) => void;
36
+ unmount: () => Promise<void>;
37
+ rerender: (element: ReactNode) => Promise<void>;
54
38
  debug: () => void;
55
39
  }
@@ -1,8 +1,9 @@
1
1
  import { call } from "@gtkx/native";
2
- import { getWidgetPtr, hasGetText, hasSetText } from "./widget.js";
2
+ import { hasGetText, hasSetText } from "./widget.js";
3
+ const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
3
4
  const emitSignal = (widget, signalName) => {
4
5
  call("libgobject-2.0.so.0", "g_signal_emit_by_name", [
5
- { type: { type: "gobject" }, value: getWidgetPtr(widget) },
6
+ { type: { type: "gobject" }, value: widget.ptr },
6
7
  { type: { type: "string" }, value: signalName },
7
8
  ], { type: "undefined" });
8
9
  };
@@ -10,10 +11,13 @@ const createUserEventInstance = (_options) => {
10
11
  return {
11
12
  click: async (element) => {
12
13
  emitSignal(element, "clicked");
14
+ await tick();
13
15
  },
14
16
  dblClick: async (element) => {
15
17
  emitSignal(element, "clicked");
18
+ await tick();
16
19
  emitSignal(element, "clicked");
20
+ await tick();
17
21
  },
18
22
  type: async (element, text) => {
19
23
  if (!hasSetText(element)) {
@@ -21,12 +25,14 @@ const createUserEventInstance = (_options) => {
21
25
  }
22
26
  const currentText = hasGetText(element) ? element.getText() : "";
23
27
  element.setText(currentText + text);
28
+ await tick();
24
29
  },
25
30
  clear: async (element) => {
26
31
  if (!hasSetText(element)) {
27
32
  throw new Error("Cannot clear element: no setText method available");
28
33
  }
29
34
  element.setText("");
35
+ await tick();
30
36
  },
31
37
  };
32
38
  };
@@ -34,10 +40,13 @@ export const userEvent = {
34
40
  setup: (options) => createUserEventInstance(options),
35
41
  click: async (element) => {
36
42
  emitSignal(element, "clicked");
43
+ await tick();
37
44
  },
38
45
  dblClick: async (element) => {
39
46
  emitSignal(element, "clicked");
47
+ await tick();
40
48
  emitSignal(element, "clicked");
49
+ await tick();
41
50
  },
42
51
  type: async (element, text) => {
43
52
  if (!hasSetText(element)) {
@@ -45,11 +54,13 @@ export const userEvent = {
45
54
  }
46
55
  const currentText = hasGetText(element) ? element.getText() : "";
47
56
  element.setText(currentText + text);
57
+ await tick();
48
58
  },
49
59
  clear: async (element) => {
50
60
  if (!hasSetText(element)) {
51
61
  throw new Error("Cannot clear element: no setText method available");
52
62
  }
53
63
  element.setText("");
64
+ await tick();
54
65
  },
55
66
  };
package/dist/widget.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type * as Gtk from "@gtkx/ffi/gtk";
2
- export declare const getWidgetPtr: (widget: Gtk.Widget) => unknown;
3
1
  type WidgetWithSetText = {
4
2
  setText: (text: string) => void;
5
3
  };
package/dist/widget.js CHANGED
@@ -1,5 +1,2 @@
1
- export const getWidgetPtr = (widget) => {
2
- return widget.ptr;
3
- };
4
1
  export const hasSetText = (widget) => typeof widget.setText === "function";
5
2
  export const hasGetText = (widget) => typeof widget.getText === "function";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/testing",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Testing utilities for GTKX applications",
5
5
  "keywords": [
6
6
  "gtk",
@@ -32,9 +32,9 @@
32
32
  "dist"
33
33
  ],
34
34
  "dependencies": {
35
- "@gtkx/ffi": "0.1.33",
36
- "@gtkx/native": "0.1.33",
37
- "@gtkx/react": "0.1.33"
35
+ "@gtkx/ffi": "0.1.35",
36
+ "@gtkx/react": "0.1.35",
37
+ "@gtkx/native": "0.1.35"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsc -b && cp ../../README.md .",