@gtkx/testing 0.15.0 → 0.16.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 CHANGED
@@ -71,6 +71,7 @@ render(<App />, "com.example.counter");
71
71
  - **React 19** — Hooks, concurrent features, and the component model you know
72
72
  - **Native GTK4 widgets** — Real native controls, not web components in a webview
73
73
  - **Adwaita support** — Modern GNOME styling with Libadwaita components
74
+ - **Declarative animations** — Framer Motion-like API using native Adwaita animations
74
75
  - **Hot Module Replacement** — Fast refresh during development
75
76
  - **TypeScript first** — Full type safety with auto-generated bindings
76
77
  - **CSS-in-JS styling** — Familiar styling patterns adapted for GTK
@@ -1,4 +1,5 @@
1
1
  import { getNativeId } from "@gtkx/ffi";
2
+ import * as Gtk from "@gtkx/ffi/gtk";
2
3
  import { formatRole } from "./role-helpers.js";
3
4
  import { isApplication } from "./traversal.js";
4
5
  import { getWidgetText } from "./widget-text.js";
@@ -105,7 +106,7 @@ const printWidget = (widget, colors, depth, includeIds) => {
105
106
  };
106
107
  const printContainer = (container, colors, includeIds) => {
107
108
  if (isApplication(container)) {
108
- const windows = container.getWindows();
109
+ const windows = Gtk.Window.listToplevels();
109
110
  return windows.map((window) => printWidget(window, colors, 0, includeIds)).join("");
110
111
  }
111
112
  return printWidget(container, colors, 0, includeIds);
package/dist/render.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { discardAllBatches, start, stop } from "@gtkx/ffi";
2
+ import { start, stop } from "@gtkx/ffi";
3
3
  import * as Gio from "@gtkx/ffi/gio";
4
4
  import { ApplicationContext, GtkApplicationWindow, reconciler } from "@gtkx/react";
5
5
  import { bindQueries } from "./bind-queries.js";
@@ -20,7 +20,6 @@ const update = async (instance, element, fiberRoot) => {
20
20
  }
21
21
  };
22
22
  const handleError = (error) => {
23
- discardAllBatches();
24
23
  lastRenderError = error;
25
24
  };
26
25
  const ensureInitialized = () => {
package/dist/screen.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type * as Gtk from "@gtkx/ffi/gtk";
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
2
  import { type ScreenshotOptions } from "./screenshot.js";
3
3
  import type { ScreenshotResult } from "./types.js";
4
4
  /** @internal */
package/dist/screen.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
+ import * as Gtk from "@gtkx/ffi/gtk";
4
5
  import { bindQueries } from "./bind-queries.js";
5
6
  import { prettyWidget } from "./pretty-widget.js";
6
7
  import { logRoles } from "./role-helpers.js";
@@ -80,8 +81,7 @@ export const screen = {
80
81
  * ```
81
82
  */
82
83
  screenshot: async (selector, options) => {
83
- const root = getRoot();
84
- const windows = root.getWindows();
84
+ const windows = Gtk.Window.listToplevels();
85
85
  if (windows.length === 0) {
86
86
  throw new Error("No windows available for screenshot");
87
87
  }
@@ -1,4 +1,4 @@
1
- import type * as Gtk from "@gtkx/ffi/gtk";
1
+ import * as Gtk from "@gtkx/ffi/gtk";
2
2
  export type Container = Gtk.Application | Gtk.Widget;
3
3
  export declare const isApplication: (container: Container) => container is Gtk.Application;
4
4
  export declare const traverse: (container: Container) => Generator<Gtk.Widget>;
package/dist/traversal.js CHANGED
@@ -1,3 +1,4 @@
1
+ import * as Gtk from "@gtkx/ffi/gtk";
1
2
  export const isApplication = (container) => "getWindows" in container && typeof container.getWindows === "function";
2
3
  const traverseWidgetTree = function* (root) {
3
4
  yield root;
@@ -7,15 +8,15 @@ const traverseWidgetTree = function* (root) {
7
8
  child = child.getNextSibling();
8
9
  }
9
10
  };
10
- const traverseApplication = function* (app) {
11
- const windows = app.getWindows();
11
+ const traverseWindows = function* () {
12
+ const windows = Gtk.Window.listToplevels();
12
13
  for (const window of windows) {
13
14
  yield* traverseWidgetTree(window);
14
15
  }
15
16
  };
16
17
  export const traverse = function* (container) {
17
18
  if (isApplication(container)) {
18
- yield* traverseApplication(container);
19
+ yield* traverseWindows();
19
20
  }
20
21
  else {
21
22
  yield* traverseWidgetTree(container);
@@ -46,13 +46,13 @@ export declare const userEvent: {
46
46
  /**
47
47
  * Double-clicks a widget.
48
48
  *
49
- * Emits two consecutive clicked signals.
49
+ * Emits pressed/released signals with n_press=1, then n_press=2.
50
50
  */
51
51
  dblClick: (element: Gtk.Widget) => Promise<void>;
52
52
  /**
53
53
  * Triple-clicks a widget.
54
54
  *
55
- * Emits three consecutive clicked signals. Useful for text selection.
55
+ * Emits pressed/released signals with n_press=1, 2, then 3. Useful for text selection.
56
56
  */
57
57
  tripleClick: (element: Gtk.Widget) => Promise<void>;
58
58
  /**
@@ -35,14 +35,25 @@ const click = async (element) => {
35
35
  await fireEvent(element, "clicked");
36
36
  }
37
37
  };
38
+ const emitClickSequence = async (element, nPress) => {
39
+ const controller = getOrCreateController(element, Gtk.GestureClick);
40
+ for (let i = 1; i <= nPress; i++) {
41
+ const args = [
42
+ Value.newFromObject(controller),
43
+ Value.newFromInt(i),
44
+ Value.newFromDouble(0),
45
+ Value.newFromDouble(0),
46
+ ];
47
+ signalEmitv(args, getSignalId(controller, "pressed"), 0, null);
48
+ signalEmitv(args, getSignalId(controller, "released"), 0, null);
49
+ }
50
+ await tick();
51
+ };
38
52
  const dblClick = async (element) => {
39
- await fireEvent(element, "clicked");
40
- await fireEvent(element, "clicked");
53
+ await emitClickSequence(element, 2);
41
54
  };
42
55
  const tripleClick = async (element) => {
43
- await fireEvent(element, "clicked");
44
- await fireEvent(element, "clicked");
45
- await fireEvent(element, "clicked");
56
+ await emitClickSequence(element, 3);
46
57
  };
47
58
  const tab = async (element, options) => {
48
59
  const direction = options?.shift ? Gtk.DirectionType.TAB_BACKWARD : Gtk.DirectionType.TAB_FORWARD;
@@ -238,17 +249,19 @@ const parseKeyboardInput = (input) => {
238
249
  }
239
250
  return actions;
240
251
  };
252
+ let gdkModifierType = null;
241
253
  const keyboard = async (element, input) => {
254
+ gdkModifierType ??= typeFromName("GdkModifierType");
242
255
  const controller = getOrCreateController(element, Gtk.EventControllerKey);
243
256
  const actions = parseKeyboardInput(input);
244
257
  for (const action of actions) {
245
258
  const signalName = action.press ? "key-pressed" : "key-released";
246
- const returnValue = Value.newFromBoolean(false);
259
+ const returnValue = action.press ? Value.newFromBoolean(false) : null;
247
260
  signalEmitv([
248
261
  Value.newFromObject(controller),
249
262
  Value.newFromUint(action.keyval),
250
263
  Value.newFromUint(0),
251
- Value.newFromUint(0),
264
+ Value.newFromFlags(gdkModifierType, 0),
252
265
  ], getSignalId(controller, signalName), 0, returnValue);
253
266
  if (action.press && action.keyval === Gdk.KEY_Return && isEditable(element)) {
254
267
  await fireEvent(element, "activate");
@@ -314,13 +327,13 @@ export const userEvent = {
314
327
  /**
315
328
  * Double-clicks a widget.
316
329
  *
317
- * Emits two consecutive clicked signals.
330
+ * Emits pressed/released signals with n_press=1, then n_press=2.
318
331
  */
319
332
  dblClick,
320
333
  /**
321
334
  * Triple-clicks a widget.
322
335
  *
323
- * Emits three consecutive clicked signals. Useful for text selection.
336
+ * Emits pressed/released signals with n_press=1, 2, then 3. Useful for text selection.
324
337
  */
325
338
  tripleClick,
326
339
  /**
@@ -1,4 +1,4 @@
1
- import { getNativeObject } from "@gtkx/ffi";
1
+ import { getNativeInterface } from "@gtkx/ffi";
2
2
  import * as Gtk from "@gtkx/ffi/gtk";
3
3
  const ROLES_WITH_INTERNAL_LABELS = new Set([
4
4
  Gtk.AccessibleRole.BUTTON,
@@ -77,7 +77,7 @@ export const getWidgetText = (widget) => {
77
77
  case Gtk.AccessibleRole.TEXT_BOX:
78
78
  case Gtk.AccessibleRole.SEARCH_BOX:
79
79
  case Gtk.AccessibleRole.SPIN_BUTTON:
80
- return getNativeObject(widget.handle, Gtk.Editable).getText() ?? null;
80
+ return getNativeInterface(widget, Gtk.Editable)?.getText() ?? null;
81
81
  case Gtk.AccessibleRole.GROUP:
82
82
  return widget.getLabel?.() ?? null;
83
83
  case Gtk.AccessibleRole.WINDOW:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/testing",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Testing utilities for GTKX applications",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -36,13 +36,13 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@gtkx/ffi": "0.15.0",
40
- "@gtkx/react": "0.15.0"
39
+ "@gtkx/ffi": "0.16.0",
40
+ "@gtkx/react": "0.16.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/react-reconciler": "^0.32.3",
44
44
  "react-reconciler": "^0.33.0",
45
- "@gtkx/vitest": "0.15.0"
45
+ "@gtkx/vitest": "0.16.0"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsc -b && cp ../../README.md .",