@gtkx/testing 0.10.5 → 0.11.0
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 +1 -1
- package/dist/fire-event.js +5 -1
- package/dist/queries.js +16 -21
- package/dist/render.js +7 -10
- package/dist/user-event.js +6 -12
- package/dist/wait-for.d.ts +1 -1
- package/dist/wait-for.js +2 -2
- package/dist/widget.js +2 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/main/logo.svg" alt="GTKX" width="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/main/logo.svg" alt="GTKX" width="60" height="60">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">GTKX</h1>
|
package/dist/fire-event.js
CHANGED
|
@@ -21,6 +21,10 @@ import { tick } from "./timing.js";
|
|
|
21
21
|
* @see {@link userEvent} for high-level user interactions
|
|
22
22
|
*/
|
|
23
23
|
export const fireEvent = async (element, signalName, ...args) => {
|
|
24
|
-
call("libgobject-2.0.so.0", "g_signal_emit_by_name", [
|
|
24
|
+
call("libgobject-2.0.so.0", "g_signal_emit_by_name", [
|
|
25
|
+
{ type: { type: "gobject", ownership: "none" }, value: element.id },
|
|
26
|
+
{ type: { type: "string", ownership: "none" }, value: signalName },
|
|
27
|
+
...args,
|
|
28
|
+
], { type: "undefined" });
|
|
25
29
|
await tick();
|
|
26
30
|
};
|
package/dist/queries.js
CHANGED
|
@@ -50,16 +50,17 @@ const ROLES_WITH_INTERNAL_LABELS = new Set([
|
|
|
50
50
|
Gtk.AccessibleRole.LINK,
|
|
51
51
|
]);
|
|
52
52
|
const isInternalLabel = (widget) => {
|
|
53
|
-
|
|
54
|
-
if (!accessible || accessible.getAccessibleRole() !== Gtk.AccessibleRole.LABEL)
|
|
53
|
+
if (widget.getAccessibleRole() !== Gtk.AccessibleRole.LABEL)
|
|
55
54
|
return false;
|
|
56
55
|
const parent = widget.getParent();
|
|
57
56
|
if (!parent)
|
|
58
57
|
return false;
|
|
59
|
-
|
|
60
|
-
if (!parentAccessible)
|
|
58
|
+
if (parent.getAccessibleRole === undefined)
|
|
61
59
|
return false;
|
|
62
|
-
|
|
60
|
+
const parentRole = parent.getAccessibleRole();
|
|
61
|
+
if (!parentRole)
|
|
62
|
+
return false;
|
|
63
|
+
return ROLES_WITH_INTERNAL_LABELS.has(parentRole);
|
|
63
64
|
};
|
|
64
65
|
const getLabelText = (widget) => {
|
|
65
66
|
const asLabel = widget;
|
|
@@ -70,8 +71,7 @@ const collectChildLabels = (widget) => {
|
|
|
70
71
|
const labels = [];
|
|
71
72
|
let child = widget.getFirstChild();
|
|
72
73
|
while (child) {
|
|
73
|
-
|
|
74
|
-
if (childAccessible?.getAccessibleRole() === Gtk.AccessibleRole.LABEL) {
|
|
74
|
+
if (child.getAccessibleRole() === Gtk.AccessibleRole.LABEL) {
|
|
75
75
|
const labelText = getLabelText(child);
|
|
76
76
|
if (labelText)
|
|
77
77
|
labels.push(labelText);
|
|
@@ -84,7 +84,7 @@ const collectChildLabels = (widget) => {
|
|
|
84
84
|
const getWidgetText = (widget) => {
|
|
85
85
|
if (isInternalLabel(widget))
|
|
86
86
|
return null;
|
|
87
|
-
const role =
|
|
87
|
+
const role = widget.getAccessibleRole();
|
|
88
88
|
if (role === undefined)
|
|
89
89
|
return null;
|
|
90
90
|
switch (role) {
|
|
@@ -109,7 +109,7 @@ const getWidgetText = (widget) => {
|
|
|
109
109
|
case Gtk.AccessibleRole.TEXT_BOX:
|
|
110
110
|
case Gtk.AccessibleRole.SEARCH_BOX:
|
|
111
111
|
case Gtk.AccessibleRole.SPIN_BUTTON:
|
|
112
|
-
return getNativeObject(widget.id, Gtk.Editable)
|
|
112
|
+
return getNativeObject(widget.id, Gtk.Editable).getText() ?? null;
|
|
113
113
|
case Gtk.AccessibleRole.GROUP:
|
|
114
114
|
return widget.getLabel?.() ?? null;
|
|
115
115
|
case Gtk.AccessibleRole.WINDOW:
|
|
@@ -135,10 +135,7 @@ const getWidgetTestId = (widget) => {
|
|
|
135
135
|
return widget.getName();
|
|
136
136
|
};
|
|
137
137
|
const getWidgetCheckedState = (widget) => {
|
|
138
|
-
const
|
|
139
|
-
if (!accessible)
|
|
140
|
-
return undefined;
|
|
141
|
-
const role = accessible.getAccessibleRole();
|
|
138
|
+
const role = widget.getAccessibleRole();
|
|
142
139
|
switch (role) {
|
|
143
140
|
case Gtk.AccessibleRole.CHECKBOX:
|
|
144
141
|
case Gtk.AccessibleRole.RADIO:
|
|
@@ -148,19 +145,18 @@ const getWidgetCheckedState = (widget) => {
|
|
|
148
145
|
case Gtk.AccessibleRole.SWITCH:
|
|
149
146
|
return widget.getActive();
|
|
150
147
|
default:
|
|
148
|
+
return null;
|
|
151
149
|
}
|
|
152
150
|
};
|
|
153
151
|
const getWidgetExpandedState = (widget) => {
|
|
154
|
-
const
|
|
155
|
-
if (!accessible)
|
|
156
|
-
return undefined;
|
|
157
|
-
const role = accessible.getAccessibleRole();
|
|
152
|
+
const role = widget.getAccessibleRole();
|
|
158
153
|
if (role === Gtk.AccessibleRole.BUTTON) {
|
|
159
154
|
const parent = widget.getParent();
|
|
160
155
|
if (!parent)
|
|
161
|
-
return
|
|
162
|
-
return parent.getExpanded?.();
|
|
156
|
+
return null;
|
|
157
|
+
return parent.getExpanded?.() ?? null;
|
|
163
158
|
}
|
|
159
|
+
return null;
|
|
164
160
|
};
|
|
165
161
|
const matchByRoleOptions = (widget, options) => {
|
|
166
162
|
if (!options)
|
|
@@ -201,8 +197,7 @@ const formatByRoleError = (role, options) => {
|
|
|
201
197
|
};
|
|
202
198
|
const getAllByRole = (container, role, options) => {
|
|
203
199
|
const matches = findAll(container, (node) => {
|
|
204
|
-
|
|
205
|
-
if (!accessible || accessible.getAccessibleRole() !== role)
|
|
200
|
+
if (node.getAccessibleRole() !== role)
|
|
206
201
|
return false;
|
|
207
202
|
return matchByRoleOptions(node, options);
|
|
208
203
|
});
|
package/dist/render.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { discardAllBatches,
|
|
2
|
+
import { discardAllBatches, start } from "@gtkx/ffi";
|
|
3
|
+
import * as Gio from "@gtkx/ffi/gio";
|
|
3
4
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
4
5
|
import { ApplicationContext, GtkApplicationWindow, reconciler } from "@gtkx/react";
|
|
5
6
|
import * as queries from "./queries.js";
|
|
@@ -9,14 +10,10 @@ import { hasLabel } from "./widget.js";
|
|
|
9
10
|
let application = null;
|
|
10
11
|
let container = null;
|
|
11
12
|
let lastRenderError = null;
|
|
12
|
-
const APP_ID = `com.gtkx.test${process.pid}`;
|
|
13
13
|
const getWidgetLabel = (widget) => {
|
|
14
14
|
if (!hasLabel(widget))
|
|
15
15
|
return null;
|
|
16
|
-
const
|
|
17
|
-
if (!accessible)
|
|
18
|
-
return null;
|
|
19
|
-
const role = accessible.getAccessibleRole();
|
|
16
|
+
const role = widget.getAccessibleRole();
|
|
20
17
|
if (role === Gtk.AccessibleRole.LABEL) {
|
|
21
18
|
return widget.getLabel?.() ?? null;
|
|
22
19
|
}
|
|
@@ -24,11 +21,11 @@ const getWidgetLabel = (widget) => {
|
|
|
24
21
|
};
|
|
25
22
|
const printWidgetTree = (root, indent = 0) => {
|
|
26
23
|
const prefix = " ".repeat(indent);
|
|
27
|
-
const
|
|
28
|
-
const
|
|
24
|
+
const role = root.getAccessibleRole();
|
|
25
|
+
const roleName = role !== undefined ? (Gtk.AccessibleRole[role] ?? "UNKNOWN") : "UNKNOWN";
|
|
29
26
|
const labelText = getWidgetLabel(root);
|
|
30
27
|
const label = labelText ? ` label="${labelText}"` : "";
|
|
31
|
-
let result = `${prefix}<${root.constructor.name} role=${
|
|
28
|
+
let result = `${prefix}<${root.constructor.name} role=${roleName}${label}>\n`;
|
|
32
29
|
let child = root.getFirstChild();
|
|
33
30
|
while (child) {
|
|
34
31
|
result += printWidgetTree(child, indent + 1);
|
|
@@ -51,7 +48,7 @@ const handleError = (error) => {
|
|
|
51
48
|
lastRenderError = error;
|
|
52
49
|
};
|
|
53
50
|
const ensureInitialized = () => {
|
|
54
|
-
application = start(
|
|
51
|
+
application = start("org.gtkx.testing", Gio.ApplicationFlags.NON_UNIQUE);
|
|
55
52
|
if (!container) {
|
|
56
53
|
const instance = reconciler.getInstance();
|
|
57
54
|
container = instance.createContainer(application, 1, null, false, null, "", handleError, handleError, () => { }, () => { }, null);
|
package/dist/user-event.js
CHANGED
|
@@ -10,14 +10,11 @@ const TOGGLEABLE_ROLES = new Set([
|
|
|
10
10
|
Gtk.AccessibleRole.SWITCH,
|
|
11
11
|
]);
|
|
12
12
|
const isToggleable = (widget) => {
|
|
13
|
-
|
|
14
|
-
if (!accessible)
|
|
15
|
-
return false;
|
|
16
|
-
return TOGGLEABLE_ROLES.has(accessible.getAccessibleRole());
|
|
13
|
+
return TOGGLEABLE_ROLES.has(widget.getAccessibleRole());
|
|
17
14
|
};
|
|
18
15
|
const click = async (element) => {
|
|
19
16
|
if (isToggleable(element)) {
|
|
20
|
-
const role =
|
|
17
|
+
const role = element.getAccessibleRole();
|
|
21
18
|
if (role === Gtk.AccessibleRole.CHECKBOX || role === Gtk.AccessibleRole.RADIO) {
|
|
22
19
|
const checkButton = element;
|
|
23
20
|
checkButton.setActive(!checkButton.getActive());
|
|
@@ -62,8 +59,6 @@ const type = async (element, text) => {
|
|
|
62
59
|
throw new Error("Cannot type into element: expected editable widget (TEXT_BOX, SEARCH_BOX, or SPIN_BUTTON)");
|
|
63
60
|
}
|
|
64
61
|
const editable = getNativeObject(element.id, Gtk.Editable);
|
|
65
|
-
if (!editable)
|
|
66
|
-
return;
|
|
67
62
|
const currentText = editable.getText();
|
|
68
63
|
editable.setText(currentText + text);
|
|
69
64
|
await tick();
|
|
@@ -77,10 +72,9 @@ const clear = async (element) => {
|
|
|
77
72
|
};
|
|
78
73
|
const SELECTABLE_ROLES = new Set([Gtk.AccessibleRole.COMBO_BOX, Gtk.AccessibleRole.LIST]);
|
|
79
74
|
const isSelectable = (widget) => {
|
|
80
|
-
|
|
81
|
-
if (!accessible)
|
|
75
|
+
if (!widget)
|
|
82
76
|
return false;
|
|
83
|
-
return SELECTABLE_ROLES.has(
|
|
77
|
+
return SELECTABLE_ROLES.has(widget.getAccessibleRole());
|
|
84
78
|
};
|
|
85
79
|
const selectListViewItems = (selectionModel, positions, exclusive) => {
|
|
86
80
|
if (positions.length === 0) {
|
|
@@ -114,7 +108,7 @@ const selectOptions = async (element, values) => {
|
|
|
114
108
|
if (!isSelectable(element)) {
|
|
115
109
|
throw new Error("Cannot select options: expected selectable widget (COMBO_BOX or LIST)");
|
|
116
110
|
}
|
|
117
|
-
const role =
|
|
111
|
+
const role = element.getAccessibleRole();
|
|
118
112
|
if (role === Gtk.AccessibleRole.COMBO_BOX) {
|
|
119
113
|
if (Array.isArray(values) && values.length > 1) {
|
|
120
114
|
throw new Error("Cannot select multiple options: ComboBox only supports single selection");
|
|
@@ -148,7 +142,7 @@ const deselectOptions = async (element, values) => {
|
|
|
148
142
|
await tick();
|
|
149
143
|
return;
|
|
150
144
|
}
|
|
151
|
-
const role =
|
|
145
|
+
const role = element.getAccessibleRole();
|
|
152
146
|
if (role !== Gtk.AccessibleRole.LIST) {
|
|
153
147
|
throw new Error("Cannot deselect options: only ListBox supports deselection");
|
|
154
148
|
}
|
package/dist/wait-for.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { WaitForOptions } from "./types.js";
|
|
|
19
19
|
* }, { timeout: 2000 });
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
export declare const waitFor: <T>(callback: () => T
|
|
22
|
+
export declare const waitFor: <T>(callback: () => T | Promise<T>, options?: WaitForOptions) => Promise<T>;
|
|
23
23
|
type ElementOrCallback = Gtk.Widget | (() => Gtk.Widget | null);
|
|
24
24
|
/**
|
|
25
25
|
* Waits for an element to be removed from the widget tree.
|
package/dist/wait-for.js
CHANGED
|
@@ -25,7 +25,7 @@ export const waitFor = async (callback, options) => {
|
|
|
25
25
|
let lastError = null;
|
|
26
26
|
while (Date.now() - startTime < timeout) {
|
|
27
27
|
try {
|
|
28
|
-
return callback();
|
|
28
|
+
return await callback();
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
31
|
lastError = error;
|
|
@@ -75,7 +75,7 @@ const isElementRemoved = (element) => {
|
|
|
75
75
|
export const waitForElementToBeRemoved = async (elementOrCallback, options) => {
|
|
76
76
|
const { timeout = DEFAULT_TIMEOUT, interval = DEFAULT_INTERVAL, onTimeout } = options ?? {};
|
|
77
77
|
const initialElement = getElement(elementOrCallback);
|
|
78
|
-
if (initialElement === null) {
|
|
78
|
+
if (initialElement === null || isElementRemoved(initialElement)) {
|
|
79
79
|
throw new Error("Elements already removed: waitForElementToBeRemoved requires elements to be present initially");
|
|
80
80
|
}
|
|
81
81
|
const startTime = Date.now();
|
package/dist/widget.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getNativeObject } from "@gtkx/ffi";
|
|
2
1
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
3
2
|
const EDITABLE_ROLES = new Set([
|
|
4
3
|
Gtk.AccessibleRole.TEXT_BOX,
|
|
@@ -6,10 +5,7 @@ const EDITABLE_ROLES = new Set([
|
|
|
6
5
|
Gtk.AccessibleRole.SPIN_BUTTON,
|
|
7
6
|
]);
|
|
8
7
|
export const isEditable = (widget) => {
|
|
9
|
-
|
|
10
|
-
if (!accessible)
|
|
11
|
-
return false;
|
|
12
|
-
return EDITABLE_ROLES.has(accessible.getAccessibleRole());
|
|
8
|
+
return EDITABLE_ROLES.has(widget.getAccessibleRole());
|
|
13
9
|
};
|
|
14
10
|
const LABEL_ROLES = new Set([
|
|
15
11
|
Gtk.AccessibleRole.BUTTON,
|
|
@@ -22,8 +18,5 @@ const LABEL_ROLES = new Set([
|
|
|
22
18
|
Gtk.AccessibleRole.MENU_ITEM_RADIO,
|
|
23
19
|
]);
|
|
24
20
|
export const hasLabel = (widget) => {
|
|
25
|
-
|
|
26
|
-
if (!accessible)
|
|
27
|
-
return false;
|
|
28
|
-
return LABEL_ROLES.has(accessible.getAccessibleRole());
|
|
21
|
+
return LABEL_ROLES.has(widget.getAccessibleRole());
|
|
29
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Testing utilities for GTKX applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtk",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@gtkx/ffi": "0.
|
|
36
|
-
"@gtkx/native": "0.
|
|
37
|
-
"@gtkx/react": "0.
|
|
35
|
+
"@gtkx/ffi": "0.11.0",
|
|
36
|
+
"@gtkx/native": "0.11.0",
|
|
37
|
+
"@gtkx/react": "0.11.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/react-reconciler": "^0.32.3",
|
|
41
41
|
"react-reconciler": "^0.33.0",
|
|
42
|
-
"@gtkx/vitest": "0.
|
|
42
|
+
"@gtkx/vitest": "0.11.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc -b && cp ../../README.md .",
|