@gtkx/testing 1.0.0-rc.3 → 1.0.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 +8 -9
- package/dist/accessible-native.d.ts +16 -0
- package/dist/accessible-native.d.ts.map +1 -0
- package/dist/accessible-native.js +206 -0
- package/dist/accessible-native.js.map +1 -0
- package/dist/act.d.ts +2 -0
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js.map +1 -1
- package/dist/build-queries.d.ts +27 -10
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +39 -30
- package/dist/build-queries.js.map +1 -1
- package/dist/cleanup-registry.d.ts +1 -1
- package/dist/cleanup-registry.d.ts.map +1 -1
- package/dist/cleanup-registry.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -3
- package/dist/errors.js.map +1 -1
- package/dist/fire-event.d.ts +35 -6
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +23 -7
- package/dist/fire-event.js.map +1 -1
- package/dist/frame-sync.d.ts +2 -1
- package/dist/frame-sync.d.ts.map +1 -1
- package/dist/frame-sync.js +15 -5
- package/dist/frame-sync.js.map +1 -1
- package/dist/harness-window.d.ts +4 -0
- package/dist/harness-window.d.ts.map +1 -0
- package/dist/harness-window.js +6 -0
- package/dist/harness-window.js.map +1 -0
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +4 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -0
- package/dist/matchers.d.ts +125 -39
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +156 -18
- package/dist/matchers.js.map +1 -1
- package/dist/pretty-format.d.ts +2 -0
- package/dist/pretty-format.d.ts.map +1 -0
- package/dist/pretty-format.js +2 -0
- package/dist/pretty-format.js.map +1 -0
- package/dist/pretty-widget.d.ts +7 -3
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +74 -57
- package/dist/pretty-widget.js.map +1 -1
- package/dist/production.d.ts +8 -0
- package/dist/production.d.ts.map +1 -0
- package/dist/production.js +34 -0
- package/dist/production.js.map +1 -0
- package/dist/queries.d.ts +99 -56
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +123 -83
- package/dist/queries.js.map +1 -1
- package/dist/query-helpers.d.ts +44 -0
- package/dist/query-helpers.d.ts.map +1 -0
- package/dist/query-helpers.js +57 -0
- package/dist/query-helpers.js.map +1 -0
- package/dist/render-children.d.ts +9 -0
- package/dist/render-children.d.ts.map +1 -0
- package/dist/render-children.js +11 -0
- package/dist/render-children.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +13 -11
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +12 -3
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +13 -4
- package/dist/role-helpers.js.map +1 -1
- package/dist/role-naming.d.ts +5 -0
- package/dist/role-naming.d.ts.map +1 -0
- package/dist/role-naming.js +96 -0
- package/dist/role-naming.js.map +1 -0
- package/dist/screenshot.d.ts +12 -20
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +183 -91
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +3 -0
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +9 -1
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +26 -6
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +67 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +16 -0
- package/dist/user-event/adjustment.d.ts.map +1 -1
- package/dist/user-event/adjustment.js +34 -2
- package/dist/user-event/adjustment.js.map +1 -1
- package/dist/user-event/click.d.ts +45 -3
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +177 -39
- package/dist/user-event/click.js.map +1 -1
- package/dist/user-event/column-header-click.d.ts +4 -0
- package/dist/user-event/column-header-click.d.ts.map +1 -0
- package/dist/user-event/column-header-click.js +65 -0
- package/dist/user-event/column-header-click.js.map +1 -0
- package/dist/user-event/controller.d.ts +29 -0
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +28 -0
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/event-wrapper.d.ts.map +1 -1
- package/dist/user-event/event-wrapper.js +2 -1
- package/dist/user-event/event-wrapper.js.map +1 -1
- package/dist/user-event/gesture.d.ts +51 -0
- package/dist/user-event/gesture.d.ts.map +1 -1
- package/dist/user-event/gesture.js +47 -4
- package/dist/user-event/gesture.js.map +1 -1
- package/dist/user-event/index.d.ts +70 -2
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +43 -26
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/indexed-children.d.ts +10 -0
- package/dist/user-event/indexed-children.d.ts.map +1 -0
- package/dist/user-event/indexed-children.js +50 -0
- package/dist/user-event/indexed-children.js.map +1 -0
- package/dist/user-event/keyboard.d.ts +4 -2
- package/dist/user-event/keyboard.d.ts.map +1 -1
- package/dist/user-event/keyboard.js +46 -16
- package/dist/user-event/keyboard.js.map +1 -1
- package/dist/user-event/list-row-click.d.ts +4 -0
- package/dist/user-event/list-row-click.d.ts.map +1 -0
- package/dist/user-event/list-row-click.js +39 -0
- package/dist/user-event/list-row-click.js.map +1 -0
- package/dist/user-event/native-click.d.ts +6 -0
- package/dist/user-event/native-click.d.ts.map +1 -0
- package/dist/user-event/native-click.js +32 -0
- package/dist/user-event/native-click.js.map +1 -0
- package/dist/user-event/pointer.d.ts +1 -1
- package/dist/user-event/pointer.d.ts.map +1 -1
- package/dist/user-event/pointer.js +37 -13
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +18 -0
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +56 -30
- package/dist/user-event/selection.js.map +1 -1
- package/dist/user-event/state.d.ts +1 -1
- package/dist/user-event/state.d.ts.map +1 -1
- package/dist/user-event/state.js +1 -1
- package/dist/user-event/state.js.map +1 -1
- package/dist/user-event/text.d.ts +34 -3
- package/dist/user-event/text.d.ts.map +1 -1
- package/dist/user-event/text.js +94 -17
- package/dist/user-event/text.js.map +1 -1
- package/dist/wait-for.d.ts +2 -0
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +14 -10
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +135 -173
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/widget-getters.d.ts +8 -0
- package/dist/widget-getters.d.ts.map +1 -0
- package/dist/widget-getters.js +19 -0
- package/dist/widget-getters.js.map +1 -0
- package/dist/within.d.ts +3 -2
- package/dist/within.d.ts.map +1 -1
- package/dist/within.js +3 -2
- package/dist/within.js.map +1 -1
- package/package.json +13 -8
- package/src/accessible-native.ts +333 -0
- package/src/act.ts +2 -0
- package/src/build-queries.ts +66 -48
- package/src/cleanup-registry.ts +1 -1
- package/src/config.ts +4 -3
- package/src/errors.ts +14 -3
- package/src/fire-event.ts +61 -8
- package/src/frame-sync.ts +21 -5
- package/src/harness-window.ts +9 -0
- package/src/index.ts +18 -11
- package/src/internal.ts +11 -0
- package/src/matchers.ts +466 -64
- package/src/pretty-format.ts +1 -0
- package/src/pretty-widget.ts +100 -72
- package/src/production.ts +52 -0
- package/src/queries.ts +128 -89
- package/src/query-helpers.ts +97 -0
- package/src/render-children.ts +25 -0
- package/src/render.tsx +18 -13
- package/src/role-helpers.ts +14 -4
- package/src/role-naming.ts +99 -0
- package/src/screenshot.ts +272 -103
- package/src/suggestions.ts +3 -0
- package/src/traversal.ts +49 -6
- package/src/types.ts +69 -19
- package/src/user-event/adjustment.ts +42 -2
- package/src/user-event/click.ts +248 -46
- package/src/user-event/column-header-click.ts +90 -0
- package/src/user-event/controller.ts +29 -0
- package/src/user-event/event-wrapper.ts +3 -1
- package/src/user-event/gesture.ts +57 -6
- package/src/user-event/index.ts +119 -29
- package/src/user-event/indexed-children.ts +75 -0
- package/src/user-event/keyboard.ts +70 -20
- package/src/user-event/list-row-click.ts +57 -0
- package/src/user-event/native-click.ts +46 -0
- package/src/user-event/pointer.ts +52 -13
- package/src/user-event/selection.ts +74 -33
- package/src/user-event/state.ts +2 -2
- package/src/user-event/text.ts +121 -23
- package/src/wait-for.ts +2 -0
- package/src/widget-accessible-properties.ts +183 -214
- package/src/widget-getters.ts +32 -0
- package/src/within.ts +3 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as prettyFormat from "@vitest/pretty-format";
|
package/src/pretty-widget.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import { sortStringsBy } from "@gtkx/utils";
|
|
3
|
+
import { type Config, format, type NewPlugin, type PrettyFormatOptions } from "@vitest/pretty-format";
|
|
3
4
|
import { formatRole } from "./role-helpers.js";
|
|
4
|
-
import { type Container, roots } from "./traversal.js";
|
|
5
|
-
import {
|
|
5
|
+
import { type Container, descendants, isOnScreen, roots } from "./traversal.js";
|
|
6
|
+
import { getWidgetText } from "./widget-accessible-properties.js";
|
|
6
7
|
|
|
8
|
+
/** Produces the value of the `id` attribute printed first on a widget's opening tag. */
|
|
7
9
|
type WidgetIdResolver = (widget: Gtk.Widget) => string;
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -14,34 +16,24 @@ type PrettyWidgetOptions = {
|
|
|
14
16
|
/** Truncates the output once it exceeds this many characters. */
|
|
15
17
|
maxLength?: number;
|
|
16
18
|
/** Whether to apply ANSI color highlighting; defaults to the terminal capabilities. */
|
|
17
|
-
|
|
19
|
+
shouldHighlight?: boolean;
|
|
18
20
|
/** Resolves an `id` attribute to show for each widget. */
|
|
19
21
|
getId?: WidgetIdResolver;
|
|
20
22
|
/** Stops descending past this depth, replacing deeper children with a summary line. */
|
|
21
23
|
maxDepth?: number;
|
|
24
|
+
/** Forwarded to pretty-format, which does the printing; `indent` and `min` shape the layout. */
|
|
25
|
+
prettyFormatOptions?: PrettyFormatOptions;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
|
-
type
|
|
25
|
-
tag: (s: string) => string;
|
|
26
|
-
attr: (s: string) => string;
|
|
27
|
-
value: (s: string) => string;
|
|
28
|
-
};
|
|
28
|
+
type Color = { open: string; close: string };
|
|
29
29
|
|
|
30
30
|
type FormatContext = {
|
|
31
31
|
getId: WidgetIdResolver | undefined;
|
|
32
|
-
|
|
32
|
+
config: Config;
|
|
33
33
|
maxDepth: number | undefined;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const DEFAULT_MAX_LENGTH = 7000;
|
|
37
|
-
const INDENT = " ";
|
|
38
|
-
|
|
39
|
-
const ansi = {
|
|
40
|
-
cyan: "\u{1B}[36m",
|
|
41
|
-
yellow: "\u{1B}[33m",
|
|
42
|
-
green: "\u{1B}[32m",
|
|
43
|
-
reset: "\u{1B}[0m",
|
|
44
|
-
};
|
|
45
37
|
|
|
46
38
|
const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [string, string][] => {
|
|
47
39
|
const attrs: [string, string][] = [];
|
|
@@ -66,6 +58,10 @@ const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [s
|
|
|
66
58
|
attrs.push(["accessible-hidden", "true"]);
|
|
67
59
|
}
|
|
68
60
|
|
|
61
|
+
if (!isOnScreen(widget)) {
|
|
62
|
+
attrs.push(["mapped", "false"]);
|
|
63
|
+
}
|
|
64
|
+
|
|
69
65
|
const idAttrs = attrs.filter(([key]) => key === "id");
|
|
70
66
|
|
|
71
67
|
const otherAttrs = sortStringsBy(
|
|
@@ -76,7 +72,7 @@ const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [s
|
|
|
76
72
|
return [...idAttrs, ...otherAttrs];
|
|
77
73
|
};
|
|
78
74
|
|
|
79
|
-
const
|
|
75
|
+
const isHighlightSupported = (): boolean => {
|
|
80
76
|
if (typeof process === "undefined") {
|
|
81
77
|
return false;
|
|
82
78
|
}
|
|
@@ -92,29 +88,16 @@ const shouldHighlight = (): boolean => {
|
|
|
92
88
|
return process.stdout.isTTY;
|
|
93
89
|
};
|
|
94
90
|
|
|
95
|
-
const
|
|
96
|
-
if (!isEnabled) {
|
|
97
|
-
const identity = (s: string): string => s;
|
|
98
|
-
|
|
99
|
-
return { tag: identity, attr: identity, value: identity };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
tag: (s) => `${ansi.cyan}${s}${ansi.reset}`,
|
|
104
|
-
attr: (s) => `${ansi.yellow}${s}${ansi.reset}`,
|
|
105
|
-
value: (s) => `${ansi.green}${s}${ansi.reset}`,
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
|
|
91
|
+
const paint = (color: Color, text: string): string => `${color.open}${text}${color.close}`;
|
|
109
92
|
const escapeAttrValue = (value: string): string => value.replaceAll('"', """);
|
|
110
93
|
|
|
111
|
-
const formatAttr = (key: string, value: string, colors:
|
|
94
|
+
const formatAttr = (key: string, value: string, colors: Config["colors"]): string => {
|
|
112
95
|
const quoted = `"${escapeAttrValue(value)}"`;
|
|
113
96
|
|
|
114
|
-
return ` ${colors.
|
|
97
|
+
return ` ${paint(colors.prop, key)}=${paint(colors.value, quoted)}`;
|
|
115
98
|
};
|
|
116
99
|
|
|
117
|
-
const formatAttrs = (attrs: [string, string][], colors:
|
|
100
|
+
const formatAttrs = (attrs: [string, string][], colors: Config["colors"]): string =>
|
|
118
101
|
attrs.map(([key, value]) => formatAttr(key, value, colors)).join("");
|
|
119
102
|
|
|
120
103
|
const countChildren = (widget: Gtk.Widget): number => {
|
|
@@ -129,68 +112,114 @@ const countChildren = (widget: Gtk.Widget): number => {
|
|
|
129
112
|
return count;
|
|
130
113
|
};
|
|
131
114
|
|
|
132
|
-
const
|
|
133
|
-
const { getId, colors } = ctx;
|
|
134
|
-
const indent = INDENT.repeat(depth);
|
|
135
|
-
const count = countChildren(widget);
|
|
115
|
+
const depthLimitReason = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): string => {
|
|
136
116
|
const hint = getId ? ` (pass rootId="${getId(widget)}" or raise maxDepth to expand)` : "";
|
|
117
|
+
|
|
118
|
+
return `hidden${hint}`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const hasMappedDescendant = (widget: Gtk.Widget): boolean => {
|
|
122
|
+
for (const descendant of descendants(widget)) {
|
|
123
|
+
if (isOnScreen(descendant)) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const collapseReasonFor = (widget: Gtk.Widget, depth: number, ctx: FormatContext): string | null => {
|
|
132
|
+
if (!widget.getFirstChild()) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!isOnScreen(widget) && !hasMappedDescendant(widget)) {
|
|
137
|
+
return "not mapped";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (ctx.maxDepth !== undefined && depth >= ctx.maxDepth) {
|
|
141
|
+
return depthLimitReason(widget, ctx.getId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const formatCollapsedChildrenLine = (
|
|
148
|
+
widget: Gtk.Widget,
|
|
149
|
+
indentation: string,
|
|
150
|
+
config: Config,
|
|
151
|
+
reason: string,
|
|
152
|
+
): string => {
|
|
153
|
+
const count = countChildren(widget);
|
|
137
154
|
const plural = count === 1 ? "" : "s";
|
|
138
|
-
const summary = `… ${String(count)} child widget${plural}
|
|
155
|
+
const summary = `… ${String(count)} child widget${plural} ${reason}`;
|
|
139
156
|
|
|
140
|
-
return `${
|
|
157
|
+
return `${indentation}${config.indent}${paint(config.colors.tag, summary)}${config.spacingOuter}`;
|
|
141
158
|
};
|
|
142
159
|
|
|
143
|
-
const formatChildren = (widget: Gtk.Widget, depth: number, ctx: FormatContext): string => {
|
|
160
|
+
const formatChildren = (widget: Gtk.Widget, indentation: string, depth: number, ctx: FormatContext): string => {
|
|
144
161
|
let output = "";
|
|
145
162
|
let child = widget.getFirstChild();
|
|
146
163
|
|
|
147
164
|
while (child) {
|
|
148
|
-
output += formatWidget(child, depth + 1, ctx);
|
|
165
|
+
output += formatWidget(child, indentation + ctx.config.indent, depth + 1, ctx);
|
|
149
166
|
child = child.getNextSibling();
|
|
150
167
|
}
|
|
151
168
|
|
|
152
169
|
return output;
|
|
153
170
|
};
|
|
154
171
|
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const indent = INDENT.repeat(depth);
|
|
158
|
-
const tag = widget.constructor.name;
|
|
159
|
-
const attrs = formatAttrs(buildAttrs(widget, getId), colors);
|
|
160
|
-
const openTag = `${colors.tag("<")}${colors.tag(tag)}${attrs}${colors.tag(">")}`;
|
|
161
|
-
const closeTag = `${colors.tag("</")}${colors.tag(tag)}${colors.tag(">")}`;
|
|
162
|
-
const text = getWidgetNodeText(widget);
|
|
163
|
-
const firstChild = widget.getFirstChild();
|
|
172
|
+
const formatBody = (widget: Gtk.Widget, indentation: string, depth: number, ctx: FormatContext): string => {
|
|
173
|
+
const collapseReason = collapseReasonFor(widget, depth, ctx);
|
|
164
174
|
|
|
165
|
-
if (
|
|
166
|
-
return
|
|
175
|
+
if (collapseReason === null) {
|
|
176
|
+
return formatChildren(widget, indentation, depth, ctx);
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (text) {
|
|
172
|
-
output += `${indent}${INDENT}${text}\n`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (firstChild && maxDepth !== undefined && depth >= maxDepth) {
|
|
176
|
-
output += formatHiddenChildrenLine(widget, depth, ctx);
|
|
177
|
-
output += `${indent}${closeTag}\n`;
|
|
179
|
+
return formatCollapsedChildrenLine(widget, indentation, ctx.config, collapseReason);
|
|
180
|
+
};
|
|
178
181
|
|
|
179
|
-
|
|
182
|
+
const formatWidget = (widget: Gtk.Widget, indentation: string, depth: number, ctx: FormatContext): string => {
|
|
183
|
+
const { config } = ctx;
|
|
184
|
+
const tag = widget.constructor.name;
|
|
185
|
+
const attrs = formatAttrs(buildAttrs(widget, ctx.getId), config.colors);
|
|
186
|
+
const openTag = `${paint(config.colors.tag, "<" + tag)}${attrs}${paint(config.colors.tag, ">")}`;
|
|
187
|
+
const closeTag = paint(config.colors.tag, `</${tag}>`);
|
|
188
|
+
const text = getWidgetText(widget);
|
|
189
|
+
const openLine = `${indentation}${openTag}${config.spacingOuter}`;
|
|
190
|
+
|
|
191
|
+
if (!text && !widget.getFirstChild()) {
|
|
192
|
+
return openLine;
|
|
180
193
|
}
|
|
181
194
|
|
|
182
|
-
|
|
183
|
-
|
|
195
|
+
const textLine = text ? `${indentation}${config.indent}${text}${config.spacingOuter}` : "";
|
|
196
|
+
const body = formatBody(widget, indentation, depth, ctx);
|
|
184
197
|
|
|
185
|
-
return
|
|
198
|
+
return `${openLine}${textLine}${body}${indentation}${closeTag}${config.spacingOuter}`;
|
|
186
199
|
};
|
|
187
200
|
|
|
201
|
+
const createWidgetPlugin = (options: PrettyWidgetOptions): NewPlugin => ({
|
|
202
|
+
test: (value: unknown): boolean => value instanceof Gtk.Widget,
|
|
203
|
+
serialize: (widget: Gtk.Widget, config: Config, indentation: string): string =>
|
|
204
|
+
formatWidget(widget, indentation, 0, { getId: options.getId, config, maxDepth: options.maxDepth }),
|
|
205
|
+
});
|
|
206
|
+
|
|
188
207
|
const resolveMaxLength = (options: PrettyWidgetOptions): number => {
|
|
189
208
|
const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;
|
|
190
209
|
|
|
191
210
|
return options.maxLength ?? envLimit;
|
|
192
211
|
};
|
|
193
212
|
|
|
213
|
+
const formatOptions = (options: PrettyWidgetOptions): PrettyFormatOptions => {
|
|
214
|
+
const { plugins, ...rest } = options.prettyFormatOptions ?? {};
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
highlight: options.shouldHighlight ?? isHighlightSupported(),
|
|
218
|
+
...rest,
|
|
219
|
+
plugins: [createWidgetPlugin(options), ...(plugins ?? [])],
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
194
223
|
/**
|
|
195
224
|
* Renders a widget tree as an indented, HTML-like string showing each widget's
|
|
196
225
|
* tag, accessible attributes, and text content.
|
|
@@ -206,12 +235,11 @@ const prettyWidget = (container: Container, options: PrettyWidgetOptions = {}):
|
|
|
206
235
|
return "";
|
|
207
236
|
}
|
|
208
237
|
|
|
209
|
-
const
|
|
210
|
-
const colors = createColors(isHighlight);
|
|
238
|
+
const prettyFormatOptions = formatOptions(options);
|
|
211
239
|
let output = "";
|
|
212
240
|
|
|
213
241
|
for (const root of roots(container)) {
|
|
214
|
-
output +=
|
|
242
|
+
output += format(root, prettyFormatOptions);
|
|
215
243
|
}
|
|
216
244
|
|
|
217
245
|
if (output.length > maxLength) {
|
|
@@ -235,4 +263,4 @@ const logWidget = (container: Container | Container[], options?: PrettyWidgetOpt
|
|
|
235
263
|
}
|
|
236
264
|
};
|
|
237
265
|
|
|
238
|
-
export {
|
|
266
|
+
export { logWidget, prettyWidget, type PrettyWidgetOptions };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { createRoot, type Root } from "@gtkx/react";
|
|
4
|
+
import { createHarnessWindow } from "./harness-window.js";
|
|
5
|
+
|
|
6
|
+
type ProductionRenderResult = {
|
|
7
|
+
rerender: (element: ReactNode) => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type ActiveRender = {
|
|
11
|
+
root: Root;
|
|
12
|
+
window: Gtk.Window;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const SETTLE_TURNS = 3;
|
|
16
|
+
const activeRenders: Set<ActiveRender> = new Set();
|
|
17
|
+
|
|
18
|
+
const turn = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));
|
|
19
|
+
|
|
20
|
+
const settle = async (): Promise<void> => {
|
|
21
|
+
for (let index = 0; index < SETTLE_TURNS; index++) {
|
|
22
|
+
await turn();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const render = async (element: ReactNode): Promise<ProductionRenderResult> => {
|
|
27
|
+
const window = createHarnessWindow();
|
|
28
|
+
const root = createRoot(window);
|
|
29
|
+
activeRenders.add({ root, window });
|
|
30
|
+
root.render(element);
|
|
31
|
+
window.present();
|
|
32
|
+
await settle();
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
rerender: async (next: ReactNode): Promise<void> => {
|
|
36
|
+
root.render(next);
|
|
37
|
+
await settle();
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const cleanup = async (): Promise<void> => {
|
|
43
|
+
for (const active of activeRenders) {
|
|
44
|
+
active.root.unmount();
|
|
45
|
+
active.window.destroy();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
activeRenders.clear();
|
|
49
|
+
await settle();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { cleanup, render };
|