@gtkx/testing 1.0.0-rc.4 → 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 +5 -5
- 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/build-queries.d.ts +27 -10
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +22 -24
- 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/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -1
- 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 +88 -46
- 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 +5 -2
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +73 -56
- 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 +98 -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 +11 -9
- 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/traversal.d.ts +4 -1
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +25 -6
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +26 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +3 -0
- package/dist/user-event/adjustment.d.ts.map +1 -1
- package/dist/user-event/adjustment.js +23 -2
- package/dist/user-event/adjustment.js.map +1 -1
- package/dist/user-event/click.d.ts +43 -8
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +175 -44
- 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/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/index.d.ts +51 -10
- 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.map +1 -1
- package/dist/user-event/keyboard.js +33 -4
- 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 +33 -9
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +11 -8
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +49 -38
- package/dist/user-event/selection.js.map +1 -1
- package/dist/user-event/text.d.ts +6 -2
- package/dist/user-event/text.d.ts.map +1 -1
- package/dist/user-event/text.js +68 -18
- package/dist/user-event/text.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +14 -26
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +135 -190
- 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 +12 -7
- package/src/accessible-native.ts +333 -0
- package/src/build-queries.ts +47 -41
- package/src/cleanup-registry.ts +1 -1
- package/src/errors.ts +12 -1
- 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 +428 -70
- package/src/pretty-format.ts +1 -0
- package/src/pretty-widget.ts +97 -70
- package/src/production.ts +52 -0
- package/src/queries.ts +127 -89
- package/src/query-helpers.ts +97 -0
- package/src/render-children.ts +25 -0
- package/src/render.tsx +16 -11
- package/src/role-helpers.ts +14 -4
- package/src/role-naming.ts +99 -0
- package/src/screenshot.ts +272 -103
- package/src/traversal.ts +44 -6
- package/src/types.ts +28 -16
- package/src/user-event/adjustment.ts +29 -2
- package/src/user-event/click.ts +247 -52
- package/src/user-event/column-header-click.ts +90 -0
- package/src/user-event/event-wrapper.ts +3 -1
- package/src/user-event/index.ts +100 -37
- package/src/user-event/indexed-children.ts +75 -0
- package/src/user-event/keyboard.ts +52 -4
- 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 +48 -9
- package/src/user-event/selection.ts +67 -41
- package/src/user-event/text.ts +92 -21
- package/src/widget-accessible-properties.ts +183 -230
- package/src/widget-getters.ts +32 -0
- package/src/within.ts +3 -2
package/src/pretty-widget.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
|
|
|
7
8
|
/** Produces the value of the `id` attribute printed first on a widget's opening tag. */
|
|
8
9
|
type WidgetIdResolver = (widget: Gtk.Widget) => string;
|
|
@@ -20,29 +21,19 @@ type PrettyWidgetOptions = {
|
|
|
20
21
|
getId?: WidgetIdResolver;
|
|
21
22
|
/** Stops descending past this depth, replacing deeper children with a summary line. */
|
|
22
23
|
maxDepth?: number;
|
|
24
|
+
/** Forwarded to pretty-format, which does the printing; `indent` and `min` shape the layout. */
|
|
25
|
+
prettyFormatOptions?: PrettyFormatOptions;
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
type
|
|
26
|
-
tag: (s: string) => string;
|
|
27
|
-
attr: (s: string) => string;
|
|
28
|
-
value: (s: string) => string;
|
|
29
|
-
};
|
|
28
|
+
type Color = { open: string; close: string };
|
|
30
29
|
|
|
31
30
|
type FormatContext = {
|
|
32
31
|
getId: WidgetIdResolver | undefined;
|
|
33
|
-
|
|
32
|
+
config: Config;
|
|
34
33
|
maxDepth: number | undefined;
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
const DEFAULT_MAX_LENGTH = 7000;
|
|
38
|
-
const INDENT = " ";
|
|
39
|
-
|
|
40
|
-
const ansi = {
|
|
41
|
-
cyan: "\u{1B}[36m",
|
|
42
|
-
yellow: "\u{1B}[33m",
|
|
43
|
-
green: "\u{1B}[32m",
|
|
44
|
-
reset: "\u{1B}[0m",
|
|
45
|
-
};
|
|
46
37
|
|
|
47
38
|
const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [string, string][] => {
|
|
48
39
|
const attrs: [string, string][] = [];
|
|
@@ -67,6 +58,10 @@ const buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [s
|
|
|
67
58
|
attrs.push(["accessible-hidden", "true"]);
|
|
68
59
|
}
|
|
69
60
|
|
|
61
|
+
if (!isOnScreen(widget)) {
|
|
62
|
+
attrs.push(["mapped", "false"]);
|
|
63
|
+
}
|
|
64
|
+
|
|
70
65
|
const idAttrs = attrs.filter(([key]) => key === "id");
|
|
71
66
|
|
|
72
67
|
const otherAttrs = sortStringsBy(
|
|
@@ -93,29 +88,16 @@ const isHighlightSupported = (): boolean => {
|
|
|
93
88
|
return process.stdout.isTTY;
|
|
94
89
|
};
|
|
95
90
|
|
|
96
|
-
const
|
|
97
|
-
if (!isEnabled) {
|
|
98
|
-
const identity = (s: string): string => s;
|
|
99
|
-
|
|
100
|
-
return { tag: identity, attr: identity, value: identity };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
tag: (s) => `${ansi.cyan}${s}${ansi.reset}`,
|
|
105
|
-
attr: (s) => `${ansi.yellow}${s}${ansi.reset}`,
|
|
106
|
-
value: (s) => `${ansi.green}${s}${ansi.reset}`,
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
|
|
91
|
+
const paint = (color: Color, text: string): string => `${color.open}${text}${color.close}`;
|
|
110
92
|
const escapeAttrValue = (value: string): string => value.replaceAll('"', """);
|
|
111
93
|
|
|
112
|
-
const formatAttr = (key: string, value: string, colors:
|
|
94
|
+
const formatAttr = (key: string, value: string, colors: Config["colors"]): string => {
|
|
113
95
|
const quoted = `"${escapeAttrValue(value)}"`;
|
|
114
96
|
|
|
115
|
-
return ` ${colors.
|
|
97
|
+
return ` ${paint(colors.prop, key)}=${paint(colors.value, quoted)}`;
|
|
116
98
|
};
|
|
117
99
|
|
|
118
|
-
const formatAttrs = (attrs: [string, string][], colors:
|
|
100
|
+
const formatAttrs = (attrs: [string, string][], colors: Config["colors"]): string =>
|
|
119
101
|
attrs.map(([key, value]) => formatAttr(key, value, colors)).join("");
|
|
120
102
|
|
|
121
103
|
const countChildren = (widget: Gtk.Widget): number => {
|
|
@@ -130,68 +112,114 @@ const countChildren = (widget: Gtk.Widget): number => {
|
|
|
130
112
|
return count;
|
|
131
113
|
};
|
|
132
114
|
|
|
133
|
-
const
|
|
134
|
-
const { getId, colors } = ctx;
|
|
135
|
-
const indent = INDENT.repeat(depth);
|
|
136
|
-
const count = countChildren(widget);
|
|
115
|
+
const depthLimitReason = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): string => {
|
|
137
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);
|
|
138
154
|
const plural = count === 1 ? "" : "s";
|
|
139
|
-
const summary = `… ${String(count)} child widget${plural}
|
|
155
|
+
const summary = `… ${String(count)} child widget${plural} ${reason}`;
|
|
140
156
|
|
|
141
|
-
return `${
|
|
157
|
+
return `${indentation}${config.indent}${paint(config.colors.tag, summary)}${config.spacingOuter}`;
|
|
142
158
|
};
|
|
143
159
|
|
|
144
|
-
const formatChildren = (widget: Gtk.Widget, depth: number, ctx: FormatContext): string => {
|
|
160
|
+
const formatChildren = (widget: Gtk.Widget, indentation: string, depth: number, ctx: FormatContext): string => {
|
|
145
161
|
let output = "";
|
|
146
162
|
let child = widget.getFirstChild();
|
|
147
163
|
|
|
148
164
|
while (child) {
|
|
149
|
-
output += formatWidget(child, depth + 1, ctx);
|
|
165
|
+
output += formatWidget(child, indentation + ctx.config.indent, depth + 1, ctx);
|
|
150
166
|
child = child.getNextSibling();
|
|
151
167
|
}
|
|
152
168
|
|
|
153
169
|
return output;
|
|
154
170
|
};
|
|
155
171
|
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const indent = INDENT.repeat(depth);
|
|
159
|
-
const tag = widget.constructor.name;
|
|
160
|
-
const attrs = formatAttrs(buildAttrs(widget, getId), colors);
|
|
161
|
-
const openTag = `${colors.tag("<")}${colors.tag(tag)}${attrs}${colors.tag(">")}`;
|
|
162
|
-
const closeTag = `${colors.tag("</")}${colors.tag(tag)}${colors.tag(">")}`;
|
|
163
|
-
const text = getWidgetNodeText(widget);
|
|
164
|
-
const firstChild = widget.getFirstChild();
|
|
172
|
+
const formatBody = (widget: Gtk.Widget, indentation: string, depth: number, ctx: FormatContext): string => {
|
|
173
|
+
const collapseReason = collapseReasonFor(widget, depth, ctx);
|
|
165
174
|
|
|
166
|
-
if (
|
|
167
|
-
return
|
|
175
|
+
if (collapseReason === null) {
|
|
176
|
+
return formatChildren(widget, indentation, depth, ctx);
|
|
168
177
|
}
|
|
169
178
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (text) {
|
|
173
|
-
output += `${indent}${INDENT}${text}\n`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (firstChild && maxDepth !== undefined && depth >= maxDepth) {
|
|
177
|
-
output += formatHiddenChildrenLine(widget, depth, ctx);
|
|
178
|
-
output += `${indent}${closeTag}\n`;
|
|
179
|
+
return formatCollapsedChildrenLine(widget, indentation, ctx.config, collapseReason);
|
|
180
|
+
};
|
|
179
181
|
|
|
180
|
-
|
|
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;
|
|
181
193
|
}
|
|
182
194
|
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
const textLine = text ? `${indentation}${config.indent}${text}${config.spacingOuter}` : "";
|
|
196
|
+
const body = formatBody(widget, indentation, depth, ctx);
|
|
185
197
|
|
|
186
|
-
return
|
|
198
|
+
return `${openLine}${textLine}${body}${indentation}${closeTag}${config.spacingOuter}`;
|
|
187
199
|
};
|
|
188
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
|
+
|
|
189
207
|
const resolveMaxLength = (options: PrettyWidgetOptions): number => {
|
|
190
208
|
const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;
|
|
191
209
|
|
|
192
210
|
return options.maxLength ?? envLimit;
|
|
193
211
|
};
|
|
194
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
|
+
|
|
195
223
|
/**
|
|
196
224
|
* Renders a widget tree as an indented, HTML-like string showing each widget's
|
|
197
225
|
* tag, accessible attributes, and text content.
|
|
@@ -207,12 +235,11 @@ const prettyWidget = (container: Container, options: PrettyWidgetOptions = {}):
|
|
|
207
235
|
return "";
|
|
208
236
|
}
|
|
209
237
|
|
|
210
|
-
const
|
|
211
|
-
const colors = createColors(shouldHighlight);
|
|
238
|
+
const prettyFormatOptions = formatOptions(options);
|
|
212
239
|
let output = "";
|
|
213
240
|
|
|
214
241
|
for (const root of roots(container)) {
|
|
215
|
-
output +=
|
|
242
|
+
output += format(root, prettyFormatOptions);
|
|
216
243
|
}
|
|
217
244
|
|
|
218
245
|
if (output.length > maxLength) {
|
|
@@ -236,4 +263,4 @@ const logWidget = (container: Container | Container[], options?: PrettyWidgetOpt
|
|
|
236
263
|
}
|
|
237
264
|
};
|
|
238
265
|
|
|
239
|
-
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 };
|