@gtkx/testing 0.9.4 → 0.10.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 +55 -67
- package/dist/fire-event.d.ts +14 -8
- package/dist/fire-event.js +14 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/queries.d.ts +59 -24
- package/dist/queries.js +71 -38
- package/dist/render.d.ts +39 -10
- package/dist/render.js +63 -27
- package/dist/screen.d.ts +28 -3
- package/dist/screen.js +29 -4
- package/dist/timing.d.ts +16 -0
- package/dist/timing.js +16 -0
- package/dist/types.d.ts +55 -48
- package/dist/user-event.d.ts +79 -5
- package/dist/user-event.js +121 -20
- package/dist/wait-for.d.ts +30 -8
- package/dist/wait-for.js +31 -10
- package/dist/within.d.ts +18 -7
- package/dist/within.js +18 -7
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -1,47 +1,43 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/main/logo.svg" alt="GTKX" width="80" height="80">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">GTKX</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<strong>Build native GTK4 desktop
|
|
8
|
+
<strong>Build native GTK4 desktop applications with React and TypeScript.</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://
|
|
13
|
-
<a href="
|
|
14
|
-
<a href="
|
|
15
|
-
<a href="
|
|
12
|
+
<a href="https://www.npmjs.com/package/@gtkx/react"><img src="https://img.shields.io/npm/v/@gtkx/react.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://github.com/eugeniodepalo/gtkx/actions"><img src="https://img.shields.io/github/actions/workflow/status/eugeniodepalo/gtkx/ci.yml" alt="CI"></a>
|
|
14
|
+
<a href="https://github.com/eugeniodepalo/gtkx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MPL--2.0-blue.svg" alt="License"></a>
|
|
15
|
+
<a href="https://github.com/eugeniodepalo/gtkx/discussions"><img src="https://img.shields.io/badge/discussions-GitHub-blue" alt="GitHub Discussions"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
GTKX lets you
|
|
21
|
-
|
|
22
|
-
## Features
|
|
23
|
-
|
|
24
|
-
- **React** — Hooks, state, props, and components you already know
|
|
25
|
-
- **HMR** — Edit code and see changes instantly via Vite
|
|
26
|
-
- **Native** — Direct FFI bindings to GTK4 via Rust and libffi
|
|
27
|
-
- **CLI** — `npx @gtkx/cli@latest create` scaffolds a ready-to-go project
|
|
28
|
-
- **CSS-in-JS** — Emotion-style `css` template literals for GTK styling
|
|
29
|
-
- **Testing** — Testing Library-style `screen`, `userEvent`, and queries
|
|
20
|
+
GTKX lets you write Linux desktop applications using React. Your components render as native GTK4 widgets through a Rust FFI bridge—no webviews, no Electron, just native performance with the developer experience you already know.
|
|
30
21
|
|
|
31
22
|
## Quick Start
|
|
32
23
|
|
|
33
24
|
```bash
|
|
34
|
-
npx @gtkx/cli
|
|
25
|
+
npx @gtkx/cli create my-app
|
|
35
26
|
cd my-app
|
|
36
27
|
npm run dev
|
|
37
28
|
```
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### Example
|
|
30
|
+
## Example
|
|
42
31
|
|
|
43
32
|
```tsx
|
|
44
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
GtkApplicationWindow,
|
|
35
|
+
GtkBox,
|
|
36
|
+
GtkButton,
|
|
37
|
+
GtkLabel,
|
|
38
|
+
quit,
|
|
39
|
+
render,
|
|
40
|
+
} from "@gtkx/react";
|
|
45
41
|
import * as Gtk from "@gtkx/ffi/gtk";
|
|
46
42
|
import { useState } from "react";
|
|
47
43
|
|
|
@@ -49,67 +45,59 @@ const App = () => {
|
|
|
49
45
|
const [count, setCount] = useState(0);
|
|
50
46
|
|
|
51
47
|
return (
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
<GtkApplicationWindow
|
|
49
|
+
title="Counter"
|
|
50
|
+
defaultWidth={300}
|
|
51
|
+
defaultHeight={200}
|
|
52
|
+
onCloseRequest={quit}
|
|
53
|
+
>
|
|
54
|
+
<GtkBox
|
|
55
|
+
orientation={Gtk.Orientation.VERTICAL}
|
|
56
|
+
spacing={20}
|
|
57
|
+
valign={Gtk.Align.CENTER}
|
|
58
|
+
>
|
|
59
|
+
<GtkLabel label={`Count: ${count}`} cssClasses={["title-1"]} />
|
|
60
|
+
<GtkButton label="Increment" onClicked={() => setCount((c) => c + 1)} />
|
|
61
|
+
</GtkBox>
|
|
62
|
+
</GtkApplicationWindow>
|
|
58
63
|
);
|
|
59
64
|
};
|
|
60
65
|
|
|
61
|
-
render(<App />, "
|
|
66
|
+
render(<App />, "com.example.counter");
|
|
62
67
|
```
|
|
63
68
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
import { css } from "@gtkx/css";
|
|
68
|
-
import { Button } from "@gtkx/react";
|
|
69
|
-
|
|
70
|
-
const primary = css`
|
|
71
|
-
padding: 16px 32px;
|
|
72
|
-
border-radius: 24px;
|
|
73
|
-
background: linear-gradient(135deg, #3584e4, #9141ac);
|
|
74
|
-
color: white;
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
<Button label="Click me" cssClasses={[primary]} />;
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Testing
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
import { cleanup, render, screen, userEvent } from "@gtkx/testing";
|
|
84
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
85
|
-
|
|
86
|
-
afterEach(() => cleanup());
|
|
69
|
+
## Features
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
- **React 19** — Hooks, concurrent features, and the component model you know
|
|
72
|
+
- **Native GTK4 widgets** — Real native controls, not web components in a webview
|
|
73
|
+
- **Adwaita support** — Modern GNOME styling with Libadwaita components
|
|
74
|
+
- **Hot Module Replacement** — Fast refresh during development
|
|
75
|
+
- **TypeScript first** — Full type safety with auto-generated bindings
|
|
76
|
+
- **CSS-in-JS styling** — Familiar styling patterns adapted for GTK
|
|
77
|
+
- **Testing utilities** — Component testing similar to Testing Library
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
name: "Increment",
|
|
93
|
-
});
|
|
79
|
+
## Examples
|
|
94
80
|
|
|
95
|
-
|
|
81
|
+
Explore complete applications in the [`examples/`](./examples) directory:
|
|
96
82
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
- **[gtk-demo](./examples/gtk-demo)** — Full replica of the official GTK demo app
|
|
84
|
+
- **[hello-world](./examples/hello-world)** — Minimal application showing a counter
|
|
85
|
+
- **[todo](./examples/todo)** — Full-featured todo application with Adwaita styling and testing
|
|
86
|
+
- **[deploying](./examples/deploying)** — Example of packaging and distributing a GTKX app
|
|
100
87
|
|
|
101
|
-
##
|
|
88
|
+
## Documentation
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
- GTK4 Runtime (`gtk4` on Fedora, `libgtk-4-1` on Ubuntu)
|
|
90
|
+
Visit [https://eugeniodepalo.github.io/gtkx](https://eugeniodepalo.github.io/gtkx/) for the full documentation.
|
|
105
91
|
|
|
106
92
|
## Contributing
|
|
107
93
|
|
|
108
|
-
|
|
94
|
+
Contributions are welcome! Please see the [contributing guidelines](./CONTRIBUTING.md) and check out the [good first issues](https://github.com/eugeniodepalo/gtkx/labels/good%20first%20issue).
|
|
95
|
+
|
|
96
|
+
## Community
|
|
109
97
|
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
98
|
+
- [GitHub Discussions](https://github.com/eugeniodepalo/gtkx/discussions) — Questions, ideas, and general discussion
|
|
99
|
+
- [Issue Tracker](https://github.com/eugeniodepalo/gtkx/issues) — Bug reports and feature requests
|
|
112
100
|
|
|
113
101
|
## License
|
|
114
102
|
|
|
115
|
-
[MPL-2.0](LICENSE)
|
|
103
|
+
[MPL-2.0](./LICENSE)
|
package/dist/fire-event.d.ts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/ffi/gtk";
|
|
2
2
|
import type { Arg } from "@gtkx/native";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* like clicking or typing, use `userEvent` instead.
|
|
4
|
+
* Emits a GTK signal on a widget.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param args - Additional arguments to pass to the signal handlers
|
|
6
|
+
* Low-level utility for triggering signals directly. Prefer {@link userEvent}
|
|
7
|
+
* for common interactions like clicking and typing.
|
|
10
8
|
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
9
|
+
* @param element - The widget to emit the signal on
|
|
10
|
+
* @param signalName - GTK signal name (e.g., "clicked", "activate")
|
|
11
|
+
* @param args - Additional signal arguments
|
|
13
12
|
*
|
|
14
13
|
* @example
|
|
15
|
-
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { fireEvent } from "@gtkx/testing";
|
|
16
|
+
*
|
|
17
|
+
* // Emit custom signal
|
|
18
|
+
* await fireEvent(widget, "my-custom-signal");
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @see {@link userEvent} for high-level user interactions
|
|
16
22
|
*/
|
|
17
23
|
export declare const fireEvent: (element: Gtk.Widget, signalName: string, ...args: Arg[]) => Promise<void>;
|
package/dist/fire-event.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { call } from "@gtkx/native";
|
|
2
2
|
import { tick } from "./timing.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* like clicking or typing, use `userEvent` instead.
|
|
4
|
+
* Emits a GTK signal on a widget.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param args - Additional arguments to pass to the signal handlers
|
|
6
|
+
* Low-level utility for triggering signals directly. Prefer {@link userEvent}
|
|
7
|
+
* for common interactions like clicking and typing.
|
|
10
8
|
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
9
|
+
* @param element - The widget to emit the signal on
|
|
10
|
+
* @param signalName - GTK signal name (e.g., "clicked", "activate")
|
|
11
|
+
* @param args - Additional signal arguments
|
|
13
12
|
*
|
|
14
13
|
* @example
|
|
15
|
-
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { fireEvent } from "@gtkx/testing";
|
|
16
|
+
*
|
|
17
|
+
* // Emit custom signal
|
|
18
|
+
* await fireEvent(widget, "my-custom-signal");
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @see {@link userEvent} for high-level user interactions
|
|
16
22
|
*/
|
|
17
23
|
export const fireEvent = async (element, signalName, ...args) => {
|
|
18
24
|
call("libgobject-2.0.so.0", "g_signal_emit_by_name", [{ type: { type: "gobject" }, value: element.id }, { type: { type: "string" }, value: signalName }, ...args], { type: "undefined" });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { fireEvent } from "./fire-event.js";
|
|
2
2
|
export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, } from "./queries.js";
|
|
3
|
-
export { cleanup, render
|
|
3
|
+
export { cleanup, render } from "./render.js";
|
|
4
4
|
export { screen } from "./screen.js";
|
|
5
|
+
export { tick } from "./timing.js";
|
|
5
6
|
export type { BoundQueries, ByRoleOptions, NormalizerOptions, RenderOptions, RenderResult, TextMatch, TextMatchFunction, TextMatchOptions, WaitForOptions, } from "./types.js";
|
|
6
7
|
export type { TabOptions } from "./user-event.js";
|
|
7
8
|
export { userEvent } from "./user-event.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { fireEvent } from "./fire-event.js";
|
|
2
2
|
export { findAllByLabelText, findAllByRole, findAllByTestId, findAllByText, findByLabelText, findByRole, findByTestId, findByText, } from "./queries.js";
|
|
3
|
-
export { cleanup, render
|
|
3
|
+
export { cleanup, render } from "./render.js";
|
|
4
4
|
export { screen } from "./screen.js";
|
|
5
|
+
export { tick } from "./timing.js";
|
|
5
6
|
export { userEvent } from "./user-event.js";
|
|
6
7
|
export { waitFor, waitForElementToBeRemoved } from "./wait-for.js";
|
|
7
8
|
export { within } from "./within.js";
|
package/dist/queries.d.ts
CHANGED
|
@@ -2,66 +2,101 @@ import * as Gtk from "@gtkx/ffi/gtk";
|
|
|
2
2
|
import type { ByRoleOptions, TextMatch, TextMatchOptions } from "./types.js";
|
|
3
3
|
type Container = Gtk.Application | Gtk.Widget;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Finds a single element by accessible role.
|
|
6
|
+
*
|
|
7
|
+
* Waits for the element to appear, throwing if not found within timeout.
|
|
8
|
+
*
|
|
6
9
|
* @param container - The container to search within
|
|
7
|
-
* @param role - The accessible role to match
|
|
8
|
-
* @param options -
|
|
10
|
+
* @param role - The GTK accessible role to match
|
|
11
|
+
* @param options - Query options including name, state filters, and timeout
|
|
9
12
|
* @returns Promise resolving to the matching widget
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const button = await findByRole(container, Gtk.AccessibleRole.BUTTON, { name: "Submit" });
|
|
17
|
+
* ```
|
|
10
18
|
*/
|
|
11
19
|
export declare const findByRole: (container: Container, role: Gtk.AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget>;
|
|
12
20
|
/**
|
|
13
|
-
*
|
|
21
|
+
* Finds all elements matching an accessible role.
|
|
22
|
+
*
|
|
14
23
|
* @param container - The container to search within
|
|
15
|
-
* @param role - The accessible role to match
|
|
16
|
-
* @param options -
|
|
24
|
+
* @param role - The GTK accessible role to match
|
|
25
|
+
* @param options - Query options including name, state filters, and timeout
|
|
17
26
|
* @returns Promise resolving to array of matching widgets
|
|
18
27
|
*/
|
|
19
28
|
export declare const findAllByRole: (container: Container, role: Gtk.AccessibleRole, options?: ByRoleOptions) => Promise<Gtk.Widget[]>;
|
|
20
29
|
/**
|
|
21
|
-
*
|
|
30
|
+
* Finds a single element by its label or text content.
|
|
31
|
+
*
|
|
32
|
+
* Matches button labels, input placeholders, window titles, and other
|
|
33
|
+
* accessible text content.
|
|
34
|
+
*
|
|
22
35
|
* @param container - The container to search within
|
|
23
|
-
* @param text -
|
|
24
|
-
* @param options -
|
|
36
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
37
|
+
* @param options - Query options including normalization and timeout
|
|
25
38
|
* @returns Promise resolving to the matching widget
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* const button = await findByLabelText(container, "Click me");
|
|
43
|
+
* const input = await findByLabelText(container, /search/i);
|
|
44
|
+
* ```
|
|
26
45
|
*/
|
|
27
46
|
export declare const findByLabelText: (container: Container, text: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget>;
|
|
28
47
|
/**
|
|
29
|
-
*
|
|
48
|
+
* Finds all elements matching label or text content.
|
|
49
|
+
*
|
|
30
50
|
* @param container - The container to search within
|
|
31
|
-
* @param text -
|
|
32
|
-
* @param options -
|
|
51
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
52
|
+
* @param options - Query options including normalization and timeout
|
|
33
53
|
* @returns Promise resolving to array of matching widgets
|
|
34
54
|
*/
|
|
35
55
|
export declare const findAllByLabelText: (container: Container, text: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
|
|
36
56
|
/**
|
|
37
|
-
*
|
|
57
|
+
* Finds a single element by visible text content.
|
|
58
|
+
*
|
|
59
|
+
* Similar to {@link findByLabelText} but focuses on directly visible text.
|
|
60
|
+
*
|
|
38
61
|
* @param container - The container to search within
|
|
39
|
-
* @param text -
|
|
40
|
-
* @param options -
|
|
62
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
63
|
+
* @param options - Query options including normalization and timeout
|
|
41
64
|
* @returns Promise resolving to the matching widget
|
|
42
65
|
*/
|
|
43
66
|
export declare const findByText: (container: Container, text: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget>;
|
|
44
67
|
/**
|
|
45
|
-
*
|
|
68
|
+
* Finds all elements matching visible text content.
|
|
69
|
+
*
|
|
46
70
|
* @param container - The container to search within
|
|
47
|
-
* @param text -
|
|
48
|
-
* @param options -
|
|
71
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
72
|
+
* @param options - Query options including normalization and timeout
|
|
49
73
|
* @returns Promise resolving to array of matching widgets
|
|
50
74
|
*/
|
|
51
75
|
export declare const findAllByText: (container: Container, text: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
|
|
52
76
|
/**
|
|
53
|
-
*
|
|
77
|
+
* Finds a single element by test ID (widget name).
|
|
78
|
+
*
|
|
79
|
+
* Uses the widget's `name` property as a test identifier.
|
|
80
|
+
* Set via the `name` prop on GTKX components.
|
|
81
|
+
*
|
|
54
82
|
* @param container - The container to search within
|
|
55
|
-
* @param testId -
|
|
56
|
-
* @param options -
|
|
83
|
+
* @param testId - Test ID to match (string, RegExp, or custom matcher)
|
|
84
|
+
* @param options - Query options including normalization and timeout
|
|
57
85
|
* @returns Promise resolving to the matching widget
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```tsx
|
|
89
|
+
* // In component: <GtkButton name="submit-btn" />
|
|
90
|
+
* const button = await findByTestId(container, "submit-btn");
|
|
91
|
+
* ```
|
|
58
92
|
*/
|
|
59
93
|
export declare const findByTestId: (container: Container, testId: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget>;
|
|
60
94
|
/**
|
|
61
|
-
*
|
|
95
|
+
* Finds all elements matching a test ID pattern.
|
|
96
|
+
*
|
|
62
97
|
* @param container - The container to search within
|
|
63
|
-
* @param testId -
|
|
64
|
-
* @param options -
|
|
98
|
+
* @param testId - Test ID to match (string, RegExp, or custom matcher)
|
|
99
|
+
* @param options - Query options including normalization and timeout
|
|
65
100
|
* @returns Promise resolving to array of matching widgets
|
|
66
101
|
*/
|
|
67
102
|
export declare const findAllByTestId: (container: Container, testId: TextMatch, options?: TextMatchOptions) => Promise<Gtk.Widget[]>;
|
package/dist/queries.js
CHANGED
|
@@ -148,7 +148,6 @@ const getWidgetCheckedState = (widget) => {
|
|
|
148
148
|
case Gtk.AccessibleRole.SWITCH:
|
|
149
149
|
return widget.getActive();
|
|
150
150
|
default:
|
|
151
|
-
return undefined;
|
|
152
151
|
}
|
|
153
152
|
};
|
|
154
153
|
const getWidgetExpandedState = (widget) => {
|
|
@@ -162,7 +161,6 @@ const getWidgetExpandedState = (widget) => {
|
|
|
162
161
|
return undefined;
|
|
163
162
|
return parent.getExpanded?.();
|
|
164
163
|
}
|
|
165
|
-
return undefined;
|
|
166
164
|
};
|
|
167
165
|
const matchByRoleOptions = (widget, options) => {
|
|
168
166
|
if (!options)
|
|
@@ -186,9 +184,9 @@ const matchByRoleOptions = (widget, options) => {
|
|
|
186
184
|
};
|
|
187
185
|
const formatRole = (role) => Gtk.AccessibleRole[role] ?? String(role);
|
|
188
186
|
const formatByRoleError = (role, options) => {
|
|
189
|
-
const parts = [`role
|
|
187
|
+
const parts = [`role '${formatRole(role)}'`];
|
|
190
188
|
if (options?.name)
|
|
191
|
-
parts.push(`name
|
|
189
|
+
parts.push(`name '${options.name}'`);
|
|
192
190
|
if (options?.checked !== undefined)
|
|
193
191
|
parts.push(`checked=${options.checked}`);
|
|
194
192
|
if (options?.pressed !== undefined)
|
|
@@ -216,7 +214,7 @@ const getAllByRole = (container, role, options) => {
|
|
|
216
214
|
const getByRole = (container, role, options) => {
|
|
217
215
|
const matches = getAllByRole(container, role, options);
|
|
218
216
|
if (matches.length > 1) {
|
|
219
|
-
throw new Error(`
|
|
217
|
+
throw new Error(`Expected 1 element with ${formatByRoleError(role, options)}, found ${matches.length}`);
|
|
220
218
|
}
|
|
221
219
|
const [first] = matches;
|
|
222
220
|
if (!first)
|
|
@@ -229,18 +227,18 @@ const getAllByLabelText = (container, text, options) => {
|
|
|
229
227
|
return matchText(widgetText, text, node, options);
|
|
230
228
|
});
|
|
231
229
|
if (matches.length === 0) {
|
|
232
|
-
throw new Error(`Unable to find any elements with label text
|
|
230
|
+
throw new Error(`Unable to find any elements with label text '${text}'`);
|
|
233
231
|
}
|
|
234
232
|
return matches;
|
|
235
233
|
};
|
|
236
234
|
const getByLabelText = (container, text, options) => {
|
|
237
235
|
const matches = getAllByLabelText(container, text, options);
|
|
238
236
|
if (matches.length > 1) {
|
|
239
|
-
throw new Error(`
|
|
237
|
+
throw new Error(`Expected 1 element with label text '${text}', found ${matches.length}`);
|
|
240
238
|
}
|
|
241
239
|
const [first] = matches;
|
|
242
240
|
if (!first)
|
|
243
|
-
throw new Error(`Unable to find element with label text
|
|
241
|
+
throw new Error(`Unable to find element with label text '${text}'`);
|
|
244
242
|
return first;
|
|
245
243
|
};
|
|
246
244
|
const getAllByText = (container, text, options) => {
|
|
@@ -249,18 +247,18 @@ const getAllByText = (container, text, options) => {
|
|
|
249
247
|
return matchText(widgetText, text, node, options);
|
|
250
248
|
});
|
|
251
249
|
if (matches.length === 0) {
|
|
252
|
-
throw new Error(`Unable to find any elements with text
|
|
250
|
+
throw new Error(`Unable to find any elements with text '${text}'`);
|
|
253
251
|
}
|
|
254
252
|
return matches;
|
|
255
253
|
};
|
|
256
254
|
const getByText = (container, text, options) => {
|
|
257
255
|
const matches = getAllByText(container, text, options);
|
|
258
256
|
if (matches.length > 1) {
|
|
259
|
-
throw new Error(`
|
|
257
|
+
throw new Error(`Expected 1 element with text '${text}', found ${matches.length}`);
|
|
260
258
|
}
|
|
261
259
|
const [first] = matches;
|
|
262
260
|
if (!first)
|
|
263
|
-
throw new Error(`Unable to find element with text
|
|
261
|
+
throw new Error(`Unable to find element with text '${text}'`);
|
|
264
262
|
return first;
|
|
265
263
|
};
|
|
266
264
|
const getAllByTestId = (container, testId, options) => {
|
|
@@ -269,95 +267,130 @@ const getAllByTestId = (container, testId, options) => {
|
|
|
269
267
|
return matchText(widgetTestId, testId, node, options);
|
|
270
268
|
});
|
|
271
269
|
if (matches.length === 0) {
|
|
272
|
-
throw new Error(`Unable to find any elements with test id
|
|
270
|
+
throw new Error(`Unable to find any elements with test id '${testId}'`);
|
|
273
271
|
}
|
|
274
272
|
return matches;
|
|
275
273
|
};
|
|
276
274
|
const getByTestId = (container, testId, options) => {
|
|
277
275
|
const matches = getAllByTestId(container, testId, options);
|
|
278
276
|
if (matches.length > 1) {
|
|
279
|
-
throw new Error(`
|
|
277
|
+
throw new Error(`Expected 1 element with test id '${testId}', found ${matches.length}`);
|
|
280
278
|
}
|
|
281
279
|
const [first] = matches;
|
|
282
280
|
if (!first)
|
|
283
|
-
throw new Error(`Unable to find element with test id
|
|
281
|
+
throw new Error(`Unable to find element with test id '${testId}'`);
|
|
284
282
|
return first;
|
|
285
283
|
};
|
|
286
284
|
/**
|
|
287
|
-
*
|
|
285
|
+
* Finds a single element by accessible role.
|
|
286
|
+
*
|
|
287
|
+
* Waits for the element to appear, throwing if not found within timeout.
|
|
288
|
+
*
|
|
288
289
|
* @param container - The container to search within
|
|
289
|
-
* @param role - The accessible role to match
|
|
290
|
-
* @param options -
|
|
290
|
+
* @param role - The GTK accessible role to match
|
|
291
|
+
* @param options - Query options including name, state filters, and timeout
|
|
291
292
|
* @returns Promise resolving to the matching widget
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```tsx
|
|
296
|
+
* const button = await findByRole(container, Gtk.AccessibleRole.BUTTON, { name: "Submit" });
|
|
297
|
+
* ```
|
|
292
298
|
*/
|
|
293
299
|
export const findByRole = async (container, role, options) => waitFor(() => getByRole(container, role, options), {
|
|
294
300
|
timeout: options?.timeout,
|
|
295
301
|
});
|
|
296
302
|
/**
|
|
297
|
-
*
|
|
303
|
+
* Finds all elements matching an accessible role.
|
|
304
|
+
*
|
|
298
305
|
* @param container - The container to search within
|
|
299
|
-
* @param role - The accessible role to match
|
|
300
|
-
* @param options -
|
|
306
|
+
* @param role - The GTK accessible role to match
|
|
307
|
+
* @param options - Query options including name, state filters, and timeout
|
|
301
308
|
* @returns Promise resolving to array of matching widgets
|
|
302
309
|
*/
|
|
303
310
|
export const findAllByRole = async (container, role, options) => waitFor(() => getAllByRole(container, role, options), {
|
|
304
311
|
timeout: options?.timeout,
|
|
305
312
|
});
|
|
306
313
|
/**
|
|
307
|
-
*
|
|
314
|
+
* Finds a single element by its label or text content.
|
|
315
|
+
*
|
|
316
|
+
* Matches button labels, input placeholders, window titles, and other
|
|
317
|
+
* accessible text content.
|
|
318
|
+
*
|
|
308
319
|
* @param container - The container to search within
|
|
309
|
-
* @param text -
|
|
310
|
-
* @param options -
|
|
320
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
321
|
+
* @param options - Query options including normalization and timeout
|
|
311
322
|
* @returns Promise resolving to the matching widget
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```tsx
|
|
326
|
+
* const button = await findByLabelText(container, "Click me");
|
|
327
|
+
* const input = await findByLabelText(container, /search/i);
|
|
328
|
+
* ```
|
|
312
329
|
*/
|
|
313
330
|
export const findByLabelText = async (container, text, options) => waitFor(() => getByLabelText(container, text, options), {
|
|
314
331
|
timeout: options?.timeout,
|
|
315
332
|
});
|
|
316
333
|
/**
|
|
317
|
-
*
|
|
334
|
+
* Finds all elements matching label or text content.
|
|
335
|
+
*
|
|
318
336
|
* @param container - The container to search within
|
|
319
|
-
* @param text -
|
|
320
|
-
* @param options -
|
|
337
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
338
|
+
* @param options - Query options including normalization and timeout
|
|
321
339
|
* @returns Promise resolving to array of matching widgets
|
|
322
340
|
*/
|
|
323
341
|
export const findAllByLabelText = async (container, text, options) => waitFor(() => getAllByLabelText(container, text, options), {
|
|
324
342
|
timeout: options?.timeout,
|
|
325
343
|
});
|
|
326
344
|
/**
|
|
327
|
-
*
|
|
345
|
+
* Finds a single element by visible text content.
|
|
346
|
+
*
|
|
347
|
+
* Similar to {@link findByLabelText} but focuses on directly visible text.
|
|
348
|
+
*
|
|
328
349
|
* @param container - The container to search within
|
|
329
|
-
* @param text -
|
|
330
|
-
* @param options -
|
|
350
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
351
|
+
* @param options - Query options including normalization and timeout
|
|
331
352
|
* @returns Promise resolving to the matching widget
|
|
332
353
|
*/
|
|
333
354
|
export const findByText = async (container, text, options) => waitFor(() => getByText(container, text, options), {
|
|
334
355
|
timeout: options?.timeout,
|
|
335
356
|
});
|
|
336
357
|
/**
|
|
337
|
-
*
|
|
358
|
+
* Finds all elements matching visible text content.
|
|
359
|
+
*
|
|
338
360
|
* @param container - The container to search within
|
|
339
|
-
* @param text -
|
|
340
|
-
* @param options -
|
|
361
|
+
* @param text - Text to match (string, RegExp, or custom matcher)
|
|
362
|
+
* @param options - Query options including normalization and timeout
|
|
341
363
|
* @returns Promise resolving to array of matching widgets
|
|
342
364
|
*/
|
|
343
365
|
export const findAllByText = async (container, text, options) => waitFor(() => getAllByText(container, text, options), {
|
|
344
366
|
timeout: options?.timeout,
|
|
345
367
|
});
|
|
346
368
|
/**
|
|
347
|
-
*
|
|
369
|
+
* Finds a single element by test ID (widget name).
|
|
370
|
+
*
|
|
371
|
+
* Uses the widget's `name` property as a test identifier.
|
|
372
|
+
* Set via the `name` prop on GTKX components.
|
|
373
|
+
*
|
|
348
374
|
* @param container - The container to search within
|
|
349
|
-
* @param testId -
|
|
350
|
-
* @param options -
|
|
375
|
+
* @param testId - Test ID to match (string, RegExp, or custom matcher)
|
|
376
|
+
* @param options - Query options including normalization and timeout
|
|
351
377
|
* @returns Promise resolving to the matching widget
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```tsx
|
|
381
|
+
* // In component: <GtkButton name="submit-btn" />
|
|
382
|
+
* const button = await findByTestId(container, "submit-btn");
|
|
383
|
+
* ```
|
|
352
384
|
*/
|
|
353
385
|
export const findByTestId = async (container, testId, options) => waitFor(() => getByTestId(container, testId, options), {
|
|
354
386
|
timeout: options?.timeout,
|
|
355
387
|
});
|
|
356
388
|
/**
|
|
357
|
-
*
|
|
389
|
+
* Finds all elements matching a test ID pattern.
|
|
390
|
+
*
|
|
358
391
|
* @param container - The container to search within
|
|
359
|
-
* @param testId -
|
|
360
|
-
* @param options -
|
|
392
|
+
* @param testId - Test ID to match (string, RegExp, or custom matcher)
|
|
393
|
+
* @param options - Query options including normalization and timeout
|
|
361
394
|
* @returns Promise resolving to array of matching widgets
|
|
362
395
|
*/
|
|
363
396
|
export const findAllByTestId = async (container, testId, options) => waitFor(() => getAllByTestId(container, testId, options), {
|