@gtkx/testing 0.9.0 → 0.9.2

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
@@ -22,7 +22,7 @@ GTKX lets you build native Linux desktop applications using React and TypeScript
22
22
  ## Features
23
23
 
24
24
  - **React** — Hooks, state, props, and components you already know
25
- - **Hot Reload** — Edit code and see changes instantly via Vite
25
+ - **HMR** — Edit code and see changes instantly via Vite
26
26
  - **Native** — Direct FFI bindings to GTK4 via Rust and libffi
27
27
  - **CLI** — `npx @gtkx/cli@latest create` scaffolds a ready-to-go project
28
28
  - **CSS-in-JS** — Emotion-style `css` template literals for GTK styling
@@ -41,14 +41,7 @@ Edit your code and see changes instantly—no restart needed.
41
41
  ### Example
42
42
 
43
43
  ```tsx
44
- import {
45
- render,
46
- ApplicationWindow,
47
- Box,
48
- Button,
49
- Label,
50
- quit,
51
- } from "@gtkx/react";
44
+ import { render, ApplicationWindow, Box, Button, quit } from "@gtkx/react";
52
45
  import * as Gtk from "@gtkx/ffi/gtk";
53
46
  import { useState } from "react";
54
47
 
@@ -58,7 +51,7 @@ const App = () => {
58
51
  return (
59
52
  <ApplicationWindow title="Counter" onCloseRequest={quit}>
60
53
  <Box orientation={Gtk.Orientation.VERTICAL} spacing={12}>
61
- <Label label={`Count: ${count}`} />
54
+ {`Count: ${count}`}
62
55
  <Button label="Increment" onClicked={() => setCount((c) => c + 1)} />
63
56
  </Box>
64
57
  </ApplicationWindow>
@@ -84,8 +77,6 @@ const primary = css`
84
77
  <Button label="Click me" cssClasses={[primary]} />;
85
78
  ```
86
79
 
87
- GTK also provides built-in classes like `suggested-action`, `destructive-action`, `card`, and `heading`.
88
-
89
80
  ## Testing
90
81
 
91
82
  ```tsx
@@ -109,9 +100,8 @@ test("increments count", async () => {
109
100
 
110
101
  ## Requirements
111
102
 
112
- - Node.js 20+
103
+ - Node.js 20+ (Deno support experimental)
113
104
  - GTK4 Runtime (`gtk4` on Fedora, `libgtk-4-1` on Ubuntu)
114
- - Linux
115
105
 
116
106
  ## Contributing
117
107
 
package/dist/queries.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getInterface } from "@gtkx/ffi";
1
+ import { getNativeObject } from "@gtkx/ffi";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
3
  import { findAll } from "./traversal.js";
4
4
  import { waitFor } from "./wait-for.js";
@@ -50,13 +50,13 @@ const ROLES_WITH_INTERNAL_LABELS = new Set([
50
50
  Gtk.AccessibleRole.LINK,
51
51
  ]);
52
52
  const isInternalLabel = (widget) => {
53
- const accessible = getInterface(widget.id, Gtk.Accessible);
53
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
54
54
  if (!accessible || accessible.getAccessibleRole() !== Gtk.AccessibleRole.LABEL)
55
55
  return false;
56
56
  const parent = widget.getParent();
57
57
  if (!parent)
58
58
  return false;
59
- const parentAccessible = getInterface(parent.id, Gtk.Accessible);
59
+ const parentAccessible = getNativeObject(parent.id, Gtk.Accessible);
60
60
  if (!parentAccessible)
61
61
  return false;
62
62
  return ROLES_WITH_INTERNAL_LABELS.has(parentAccessible.getAccessibleRole());
@@ -70,7 +70,7 @@ const collectChildLabels = (widget) => {
70
70
  const labels = [];
71
71
  let child = widget.getFirstChild();
72
72
  while (child) {
73
- const childAccessible = getInterface(child.id, Gtk.Accessible);
73
+ const childAccessible = getNativeObject(child.id, Gtk.Accessible);
74
74
  if (childAccessible?.getAccessibleRole() === Gtk.AccessibleRole.LABEL) {
75
75
  const labelText = getLabelText(child);
76
76
  if (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 = getInterface(widget.id, Gtk.Accessible)?.getAccessibleRole();
87
+ const role = getNativeObject(widget.id, Gtk.Accessible)?.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 getInterface(widget.id, Gtk.Editable)?.getText() ?? null;
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,7 +135,7 @@ const getWidgetTestId = (widget) => {
135
135
  return widget.getName();
136
136
  };
137
137
  const getWidgetCheckedState = (widget) => {
138
- const accessible = getInterface(widget.id, Gtk.Accessible);
138
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
139
139
  if (!accessible)
140
140
  return undefined;
141
141
  const role = accessible.getAccessibleRole();
@@ -152,7 +152,7 @@ const getWidgetCheckedState = (widget) => {
152
152
  }
153
153
  };
154
154
  const getWidgetExpandedState = (widget) => {
155
- const accessible = getInterface(widget.id, Gtk.Accessible);
155
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
156
156
  if (!accessible)
157
157
  return undefined;
158
158
  const role = accessible.getAccessibleRole();
@@ -203,7 +203,7 @@ const formatByRoleError = (role, options) => {
203
203
  };
204
204
  const getAllByRole = (container, role, options) => {
205
205
  const matches = findAll(container, (node) => {
206
- const accessible = getInterface(node.id, Gtk.Accessible);
206
+ const accessible = getNativeObject(node.id, Gtk.Accessible);
207
207
  if (!accessible || accessible.getAccessibleRole() !== role)
208
208
  return false;
209
209
  return matchByRoleOptions(node, options);
package/dist/render.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getCurrentApp, getInterface, start, stop } from "@gtkx/ffi";
2
+ import { getApplication, getNativeObject, start, stop } from "@gtkx/ffi";
3
3
  import * as Gtk from "@gtkx/ffi/gtk";
4
4
  import { ApplicationWindow, ROOT_NODE_CONTAINER, reconciler } from "@gtkx/react";
5
5
  import * as queries from "./queries.js";
@@ -11,7 +11,7 @@ let container = null;
11
11
  const getWidgetLabel = (widget) => {
12
12
  if (!hasLabel(widget))
13
13
  return null;
14
- const accessible = getInterface(widget.id, Gtk.Accessible);
14
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
15
15
  if (!accessible)
16
16
  return null;
17
17
  const role = accessible.getAccessibleRole();
@@ -22,7 +22,7 @@ const getWidgetLabel = (widget) => {
22
22
  };
23
23
  const printWidgetTree = (root, indent = 0) => {
24
24
  const prefix = " ".repeat(indent);
25
- const accessibleRole = getInterface(root.id, Gtk.Accessible)?.getAccessibleRole();
25
+ const accessibleRole = getNativeObject(root.id, Gtk.Accessible)?.getAccessibleRole();
26
26
  const role = accessibleRole !== undefined ? (Gtk.AccessibleRole[accessibleRole] ?? "UNKNOWN") : "UNKNOWN";
27
27
  const labelText = getWidgetLabel(root);
28
28
  const label = labelText ? ` label="${labelText}"` : "";
@@ -96,7 +96,7 @@ export const render = async (element, options) => {
96
96
  */
97
97
  export const cleanup = async () => {
98
98
  if (container) {
99
- const app = getCurrentApp();
99
+ const app = getApplication();
100
100
  const instance = reconciler.getInstance();
101
101
  await update(instance, null, container);
102
102
  for (const window of app.getWindows()) {
package/dist/screen.d.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import type * as Gtk from "@gtkx/ffi/gtk";
2
2
  import type { ByRoleOptions, TextMatch, TextMatchOptions } from "./types.js";
3
- /**
4
- * Sets the root application for screen queries. Called internally by render().
5
- * @param root - The GTK application to use as query root, or null to clear
6
- */
7
3
  export declare const setScreenRoot: (root: Gtk.Application | null) => void;
8
4
  /**
9
5
  * Global screen object providing query methods bound to the current render root.
package/dist/screen.js CHANGED
@@ -1,9 +1,5 @@
1
1
  import * as queries from "./queries.js";
2
2
  let currentRoot = null;
3
- /**
4
- * Sets the root application for screen queries. Called internally by render().
5
- * @param root - The GTK application to use as query root, or null to clear
6
- */
7
3
  export const setScreenRoot = (root) => {
8
4
  currentRoot = root;
9
5
  };
package/dist/timing.d.ts CHANGED
@@ -1,4 +1 @@
1
- /**
2
- * Returns a promise that resolves on the next event loop tick.
3
- */
4
1
  export declare const tick: () => Promise<void>;
package/dist/timing.js CHANGED
@@ -1,4 +1 @@
1
- /**
2
- * Returns a promise that resolves on the next event loop tick.
3
- */
4
1
  export const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
@@ -1,10 +1,4 @@
1
1
  import type * as Gtk from "@gtkx/ffi/gtk";
2
2
  type Container = Gtk.Application | Gtk.Widget;
3
- /**
4
- * Traverses the widget tree and returns all widgets matching the predicate.
5
- * @param container - The application or widget to search within
6
- * @param predicate - Function that returns true for matching widgets
7
- * @returns Array of widgets that match the predicate
8
- */
9
3
  export declare const findAll: (container: Container, predicate: (node: Gtk.Widget) => boolean) => Gtk.Widget[];
10
4
  export {};
package/dist/traversal.js CHANGED
@@ -21,12 +21,6 @@ const traverse = function* (container) {
21
21
  yield* traverseWidgetTree(container);
22
22
  }
23
23
  };
24
- /**
25
- * Traverses the widget tree and returns all widgets matching the predicate.
26
- * @param container - The application or widget to search within
27
- * @param predicate - Function that returns true for matching widgets
28
- * @returns Array of widgets that match the predicate
29
- */
30
24
  export const findAll = (container, predicate) => {
31
25
  const results = [];
32
26
  for (const node of traverse(container)) {
@@ -1,4 +1,4 @@
1
- import { getInterface, getObject } from "@gtkx/ffi";
1
+ import { getNativeObject } from "@gtkx/ffi";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
3
  import { fireEvent } from "./fire-event.js";
4
4
  import { tick } from "./timing.js";
@@ -10,14 +10,14 @@ const TOGGLEABLE_ROLES = new Set([
10
10
  Gtk.AccessibleRole.SWITCH,
11
11
  ]);
12
12
  const isToggleable = (widget) => {
13
- const accessible = getInterface(widget.id, Gtk.Accessible);
13
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
14
14
  if (!accessible)
15
15
  return false;
16
16
  return TOGGLEABLE_ROLES.has(accessible.getAccessibleRole());
17
17
  };
18
18
  const click = async (element) => {
19
19
  if (isToggleable(element)) {
20
- const role = getInterface(element.id, Gtk.Accessible)?.getAccessibleRole();
20
+ const role = getNativeObject(element.id, Gtk.Accessible)?.getAccessibleRole();
21
21
  if (role === Gtk.AccessibleRole.CHECKBOX || role === Gtk.AccessibleRole.RADIO) {
22
22
  const checkButton = element;
23
23
  checkButton.setActive(!checkButton.getActive());
@@ -53,7 +53,7 @@ const tab = async (element, options) => {
53
53
  const direction = options?.shift ? Gtk.DirectionType.TAB_BACKWARD : Gtk.DirectionType.TAB_FORWARD;
54
54
  const root = element.getRoot();
55
55
  if (root) {
56
- getObject(root.id).childFocus(direction);
56
+ getNativeObject(root.id).childFocus(direction);
57
57
  }
58
58
  await tick();
59
59
  };
@@ -61,7 +61,7 @@ const type = async (element, text) => {
61
61
  if (!isEditable(element)) {
62
62
  throw new Error("Cannot type into element: element is not editable (TEXT_BOX, SEARCH_BOX, or SPIN_BUTTON)");
63
63
  }
64
- const editable = getInterface(element.id, Gtk.Editable);
64
+ const editable = getNativeObject(element.id, Gtk.Editable);
65
65
  if (!editable)
66
66
  return;
67
67
  const currentText = editable.getText();
@@ -72,12 +72,12 @@ const clear = async (element) => {
72
72
  if (!isEditable(element)) {
73
73
  throw new Error("Cannot clear element: element is not editable (TEXT_BOX, SEARCH_BOX, or SPIN_BUTTON)");
74
74
  }
75
- getInterface(element.id, Gtk.Editable)?.setText("");
75
+ getNativeObject(element.id, Gtk.Editable)?.setText("");
76
76
  await tick();
77
77
  };
78
78
  const SELECTABLE_ROLES = new Set([Gtk.AccessibleRole.COMBO_BOX, Gtk.AccessibleRole.LIST]);
79
79
  const isSelectable = (widget) => {
80
- const accessible = getInterface(widget.id, Gtk.Accessible);
80
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
81
81
  if (!accessible)
82
82
  return false;
83
83
  return SELECTABLE_ROLES.has(accessible.getAccessibleRole());
@@ -86,7 +86,7 @@ const selectOptions = async (element, values) => {
86
86
  if (!isSelectable(element)) {
87
87
  throw new Error("Cannot select options: element is not a selectable widget (COMBO_BOX or LIST)");
88
88
  }
89
- const role = getInterface(element.id, Gtk.Accessible)?.getAccessibleRole();
89
+ const role = getNativeObject(element.id, Gtk.Accessible)?.getAccessibleRole();
90
90
  const valueArray = Array.isArray(values) ? values : [values];
91
91
  if (role === Gtk.AccessibleRole.COMBO_BOX) {
92
92
  if (valueArray.length > 1) {
@@ -120,7 +120,7 @@ const selectOptions = async (element, values) => {
120
120
  await tick();
121
121
  };
122
122
  const deselectOptions = async (element, values) => {
123
- const role = getInterface(element.id, Gtk.Accessible)?.getAccessibleRole();
123
+ const role = getNativeObject(element.id, Gtk.Accessible)?.getAccessibleRole();
124
124
  if (role !== Gtk.AccessibleRole.LIST) {
125
125
  throw new Error("Cannot deselect options: only ListBox supports deselection");
126
126
  }
package/dist/widget.d.ts CHANGED
@@ -1,9 +1,3 @@
1
1
  import * as Gtk from "@gtkx/ffi/gtk";
2
- /**
3
- * Checks if a widget has an editable accessible role (text box, search box, or spin button).
4
- */
5
2
  export declare const isEditable: (widget: Gtk.Widget) => boolean;
6
- /**
7
- * Checks if a widget has an accessible role that supports labels.
8
- */
9
3
  export declare const hasLabel: (widget: Gtk.Widget) => boolean;
package/dist/widget.js CHANGED
@@ -1,15 +1,12 @@
1
- import { getInterface } from "@gtkx/ffi";
1
+ import { getNativeObject } from "@gtkx/ffi";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
3
  const EDITABLE_ROLES = new Set([
4
4
  Gtk.AccessibleRole.TEXT_BOX,
5
5
  Gtk.AccessibleRole.SEARCH_BOX,
6
6
  Gtk.AccessibleRole.SPIN_BUTTON,
7
7
  ]);
8
- /**
9
- * Checks if a widget has an editable accessible role (text box, search box, or spin button).
10
- */
11
8
  export const isEditable = (widget) => {
12
- const accessible = getInterface(widget.id, Gtk.Accessible);
9
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
13
10
  if (!accessible)
14
11
  return false;
15
12
  return EDITABLE_ROLES.has(accessible.getAccessibleRole());
@@ -24,11 +21,8 @@ const LABEL_ROLES = new Set([
24
21
  Gtk.AccessibleRole.MENU_ITEM_CHECKBOX,
25
22
  Gtk.AccessibleRole.MENU_ITEM_RADIO,
26
23
  ]);
27
- /**
28
- * Checks if a widget has an accessible role that supports labels.
29
- */
30
24
  export const hasLabel = (widget) => {
31
- const accessible = getInterface(widget.id, Gtk.Accessible);
25
+ const accessible = getNativeObject(widget.id, Gtk.Accessible);
32
26
  if (!accessible)
33
27
  return false;
34
28
  return LABEL_ROLES.has(accessible.getAccessibleRole());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/testing",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Testing utilities for GTKX applications",
5
5
  "keywords": [
6
6
  "gtk",
@@ -32,12 +32,12 @@
32
32
  "dist"
33
33
  ],
34
34
  "dependencies": {
35
- "@gtkx/ffi": "0.9.0",
36
- "@gtkx/native": "0.9.0",
37
- "@gtkx/react": "0.9.0"
35
+ "@gtkx/ffi": "0.9.2",
36
+ "@gtkx/react": "0.9.2",
37
+ "@gtkx/native": "0.9.2"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsc -b && cp ../../README.md .",
41
- "test": "if [ -n \"$CI\" ]; then vitest run; else GDK_BACKEND=x11 xvfb-run -a vitest run; fi"
41
+ "test": "../../scripts/run-tests.sh"
42
42
  }
43
43
  }