@gtkx/testing 0.20.0 → 1.0.0-rc.1
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 +122 -55
- package/dist/act.d.ts +19 -0
- package/dist/act.d.ts.map +1 -0
- package/dist/act.js +48 -0
- package/dist/act.js.map +1 -0
- package/dist/bound-queries.d.ts +32 -0
- package/dist/bound-queries.d.ts.map +1 -0
- package/dist/bound-queries.js +2 -0
- package/dist/bound-queries.js.map +1 -0
- package/dist/build-queries.d.ts +16 -0
- package/dist/build-queries.d.ts.map +1 -0
- package/dist/build-queries.js +103 -0
- package/dist/build-queries.js.map +1 -0
- package/dist/cleanup-registry.d.ts +4 -0
- package/dist/cleanup-registry.d.ts.map +1 -0
- package/dist/cleanup-registry.js +11 -0
- package/dist/cleanup-registry.js.map +1 -0
- package/dist/config.d.ts +18 -51
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -40
- package/dist/config.js.map +1 -1
- package/dist/editable.d.ts +7 -0
- package/dist/editable.d.ts.map +1 -0
- package/dist/editable.js +24 -0
- package/dist/editable.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +119 -0
- package/dist/errors.js.map +1 -0
- package/dist/fire-event.d.ts +7 -25
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +9 -29
- package/dist/fire-event.js.map +1 -1
- package/dist/frame-sync.d.ts +3 -0
- package/dist/frame-sync.d.ts.map +1 -0
- package/dist/frame-sync.js +30 -0
- package/dist/frame-sync.js.map +1 -0
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +55 -0
- package/dist/matchers.d.ts.map +1 -0
- package/dist/matchers.js +103 -0
- package/dist/matchers.js.map +1 -0
- package/dist/pretty-widget.d.ts +21 -25
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +69 -115
- package/dist/pretty-widget.js.map +1 -1
- package/dist/queries.d.ts +130 -177
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +212 -325
- package/dist/queries.js.map +1 -1
- package/dist/register-test-hooks.d.ts +2 -0
- package/dist/register-test-hooks.d.ts.map +1 -0
- package/dist/register-test-hooks.js +15 -0
- package/dist/register-test-hooks.js.map +1 -0
- package/dist/render-hook.d.ts +14 -35
- package/dist/render-hook.d.ts.map +1 -1
- package/dist/render-hook.js +8 -45
- package/dist/render-hook.js.map +1 -1
- package/dist/render.d.ts +11 -41
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +129 -118
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +16 -51
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +39 -58
- package/dist/role-helpers.js.map +1 -1
- package/dist/screen.d.ts +6 -63
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +21 -109
- package/dist/screen.js.map +1 -1
- package/dist/screenshot.d.ts +20 -23
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +89 -48
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +22 -0
- package/dist/suggestions.d.ts.map +1 -0
- package/dist/suggestions.js +56 -0
- package/dist/suggestions.js.map +1 -0
- package/dist/traversal.d.ts +10 -8
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +28 -13
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +103 -172
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +8 -0
- package/dist/user-event/adjustment.d.ts.map +1 -0
- package/dist/user-event/adjustment.js +33 -0
- package/dist/user-event/adjustment.js.map +1 -0
- package/dist/user-event/click.d.ts +7 -0
- package/dist/user-event/click.d.ts.map +1 -0
- package/dist/user-event/click.js +65 -0
- package/dist/user-event/click.js.map +1 -0
- package/dist/user-event/controller.d.ts +7 -0
- package/dist/user-event/controller.d.ts.map +1 -0
- package/dist/user-event/controller.js +28 -0
- package/dist/user-event/controller.js.map +1 -0
- package/dist/user-event/dispatch.d.ts +7 -0
- package/dist/user-event/dispatch.d.ts.map +1 -0
- package/dist/user-event/dispatch.js +10 -0
- package/dist/user-event/dispatch.js.map +1 -0
- package/dist/user-event/event-wrapper.d.ts +3 -0
- package/dist/user-event/event-wrapper.d.ts.map +1 -0
- package/dist/user-event/event-wrapper.js +48 -0
- package/dist/user-event/event-wrapper.js.map +1 -0
- package/dist/user-event/gesture.d.ts +31 -0
- package/dist/user-event/gesture.d.ts.map +1 -0
- package/dist/user-event/gesture.js +105 -0
- package/dist/user-event/gesture.js.map +1 -0
- package/dist/user-event/index.d.ts +44 -0
- package/dist/user-event/index.d.ts.map +1 -0
- package/dist/user-event/index.js +38 -0
- package/dist/user-event/index.js.map +1 -0
- package/dist/user-event/keyboard.d.ts +9 -0
- package/dist/user-event/keyboard.d.ts.map +1 -0
- package/dist/user-event/keyboard.js +173 -0
- package/dist/user-event/keyboard.js.map +1 -0
- package/dist/user-event/pointer.d.ts +6 -0
- package/dist/user-event/pointer.d.ts.map +1 -0
- package/dist/user-event/pointer.js +27 -0
- package/dist/user-event/pointer.js.map +1 -0
- package/dist/user-event/selection.d.ts +4 -0
- package/dist/user-event/selection.d.ts.map +1 -0
- package/dist/user-event/selection.js +106 -0
- package/dist/user-event/selection.js.map +1 -0
- package/dist/user-event/state.d.ts +6 -0
- package/dist/user-event/state.d.ts.map +1 -0
- package/dist/user-event/state.js +2 -0
- package/dist/user-event/state.js.map +1 -0
- package/dist/user-event/text.d.ts +17 -0
- package/dist/user-event/text.d.ts.map +1 -0
- package/dist/user-event/text.js +107 -0
- package/dist/user-event/text.js.map +1 -0
- package/dist/wait-for.d.ts +13 -32
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js +62 -73
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +33 -0
- package/dist/widget-accessible-properties.d.ts.map +1 -0
- package/dist/widget-accessible-properties.js +215 -0
- package/dist/widget-accessible-properties.js.map +1 -0
- package/dist/within.d.ts +9 -26
- package/dist/within.d.ts.map +1 -1
- package/dist/within.js +15 -25
- package/dist/within.js.map +1 -1
- package/package.json +21 -12
- package/src/act.ts +68 -0
- package/src/bound-queries.ts +36 -0
- package/src/build-queries.ts +185 -0
- package/src/cleanup-registry.ts +14 -0
- package/src/config.ts +28 -59
- package/src/editable.ts +30 -0
- package/src/errors.ts +130 -0
- package/src/fire-event.ts +11 -38
- package/src/frame-sync.ts +34 -0
- package/src/index.ts +60 -18
- package/src/matchers.ts +200 -0
- package/src/pretty-widget.ts +99 -138
- package/src/queries.ts +321 -425
- package/src/register-test-hooks.ts +16 -0
- package/src/render-hook.tsx +31 -44
- package/src/render.tsx +161 -149
- package/src/role-helpers.ts +47 -67
- package/src/screen.ts +21 -114
- package/src/screenshot.ts +104 -55
- package/src/suggestions.ts +87 -0
- package/src/traversal.ts +32 -18
- package/src/types.ts +115 -176
- package/src/user-event/adjustment.ts +49 -0
- package/src/user-event/click.ts +70 -0
- package/src/user-event/controller.ts +44 -0
- package/src/user-event/dispatch.ts +23 -0
- package/src/user-event/event-wrapper.ts +54 -0
- package/src/user-event/gesture.ts +154 -0
- package/src/user-event/index.ts +74 -0
- package/src/user-event/keyboard.ts +204 -0
- package/src/user-event/pointer.ts +34 -0
- package/src/user-event/selection.ts +122 -0
- package/src/user-event/state.ts +6 -0
- package/src/user-event/text.ts +137 -0
- package/src/wait-for.ts +79 -80
- package/src/widget-accessible-properties.ts +244 -0
- package/src/within.ts +26 -27
- package/dist/bind-queries.d.ts +0 -14
- package/dist/bind-queries.d.ts.map +0 -1
- package/dist/bind-queries.js +0 -29
- package/dist/bind-queries.js.map +0 -1
- package/dist/error-builder.d.ts +0 -28
- package/dist/error-builder.d.ts.map +0 -1
- package/dist/error-builder.js +0 -81
- package/dist/error-builder.js.map +0 -1
- package/dist/timing.d.ts +0 -18
- package/dist/timing.d.ts.map +0 -1
- package/dist/timing.js +0 -18
- package/dist/timing.js.map +0 -1
- package/dist/user-event.d.ts +0 -138
- package/dist/user-event.d.ts.map +0 -1
- package/dist/user-event.js +0 -411
- package/dist/user-event.js.map +0 -1
- package/dist/widget-text.d.ts +0 -69
- package/dist/widget-text.d.ts.map +0 -1
- package/dist/widget-text.js +0 -177
- package/dist/widget-text.js.map +0 -1
- package/dist/widget.d.ts +0 -3
- package/dist/widget.d.ts.map +0 -1
- package/dist/widget.js +0 -13
- package/dist/widget.js.map +0 -1
- package/src/bind-queries.ts +0 -52
- package/src/error-builder.ts +0 -102
- package/src/timing.ts +0 -17
- package/src/user-event.ts +0 -492
- package/src/widget-text.ts +0 -202
- package/src/widget.ts +0 -15
package/dist/matchers.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/// <reference types="@vitest/expect" />
|
|
2
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
+
import { getWidgetAccessibleName, getWidgetCheckedState, getWidgetDisplayValue, getWidgetExpandedState, getWidgetNodeText, getWidgetPlaceholderText, getWidgetPressedState, getWidgetSelectedState, getWidgetValue, } from "./widget-accessible-properties.js";
|
|
4
|
+
const asWidget = (received, matcherName) => {
|
|
5
|
+
if (!(received instanceof Gtk.Widget)) {
|
|
6
|
+
throw new TypeError(`${matcherName}: received value must be a Gtk.Widget, got ${typeof received}`);
|
|
7
|
+
}
|
|
8
|
+
return received;
|
|
9
|
+
};
|
|
10
|
+
const describeWidget = (widget) => {
|
|
11
|
+
const role = Gtk.AccessibleRole[widget.getAccessibleRole()];
|
|
12
|
+
const name = getWidgetAccessibleName(widget);
|
|
13
|
+
return name !== null ? `<${role} name=${JSON.stringify(name)}>` : `<${role}>`;
|
|
14
|
+
};
|
|
15
|
+
const matchesText = (actual, expected, mode) => {
|
|
16
|
+
if (expected instanceof RegExp) {
|
|
17
|
+
expected.lastIndex = 0;
|
|
18
|
+
return expected.test(actual);
|
|
19
|
+
}
|
|
20
|
+
return mode === "exact" ? actual === expected : actual.includes(expected);
|
|
21
|
+
};
|
|
22
|
+
const describeExpected = (expected) => expected instanceof RegExp ? String(expected) : JSON.stringify(expected);
|
|
23
|
+
const textMatcher = (matcherName, read, mode) => function (received, expected) {
|
|
24
|
+
const widget = asWidget(received, matcherName);
|
|
25
|
+
const actual = read(widget);
|
|
26
|
+
if (expected === undefined) {
|
|
27
|
+
const pass = actual !== null && actual !== "";
|
|
28
|
+
return {
|
|
29
|
+
pass,
|
|
30
|
+
message: () => `expected widget ${this.isNot ? "not " : ""}to have a non-empty value for ${matcherName}, ` +
|
|
31
|
+
`but got ${JSON.stringify(actual)}\n${describeWidget(widget)}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const pass = actual !== null && matchesText(actual, expected, mode);
|
|
35
|
+
return {
|
|
36
|
+
pass,
|
|
37
|
+
message: () => `expected widget ${this.isNot ? "not " : ""}${matcherName} ${describeExpected(expected)}, ` +
|
|
38
|
+
`but received ${JSON.stringify(actual)}\n${describeWidget(widget)}`,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const booleanStateMatcher = (matcherName, stateName, read) => function (received) {
|
|
42
|
+
const widget = asWidget(received, matcherName);
|
|
43
|
+
const state = read(widget);
|
|
44
|
+
if (state === null) {
|
|
45
|
+
throw new Error(`${matcherName}: widget does not expose a ${stateName} state ` +
|
|
46
|
+
`(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\n${describeWidget(widget)}`);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
pass: state,
|
|
50
|
+
message: () => `expected widget ${this.isNot ? "not " : ""}to be ${stateName}\n${describeWidget(widget)}`,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export const toHaveDisplayValue = textMatcher("toHaveDisplayValue", getWidgetDisplayValue, "exact");
|
|
54
|
+
export const toHaveTextContent = textMatcher("toHaveTextContent", (widget) => getWidgetNodeText(widget) ?? getWidgetAccessibleName(widget), "substring");
|
|
55
|
+
export const toHaveAccessibleName = textMatcher("toHaveAccessibleName", getWidgetAccessibleName, "exact");
|
|
56
|
+
export const toHavePlaceholderText = textMatcher("toHavePlaceholderText", getWidgetPlaceholderText, "exact");
|
|
57
|
+
export const toBeChecked = booleanStateMatcher("toBeChecked", "checked", getWidgetCheckedState);
|
|
58
|
+
export const toBePressed = booleanStateMatcher("toBePressed", "pressed", getWidgetPressedState);
|
|
59
|
+
export const toBeExpanded = booleanStateMatcher("toBeExpanded", "expanded", getWidgetExpandedState);
|
|
60
|
+
export const toBeSelected = booleanStateMatcher("toBeSelected", "selected", getWidgetSelectedState);
|
|
61
|
+
export const toHaveValue = function (received, expected) {
|
|
62
|
+
const widget = asWidget(received, "toHaveValue");
|
|
63
|
+
const actual = getWidgetValue(widget).now;
|
|
64
|
+
if (actual === null) {
|
|
65
|
+
throw new Error(`toHaveValue: widget does not expose a numeric value ` +
|
|
66
|
+
`(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\n${describeWidget(widget)}`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
pass: actual === expected,
|
|
70
|
+
message: () => `expected widget ${this.isNot ? "not " : ""}to have value ${expected}, but received ${actual}\n${describeWidget(widget)}`,
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
/** The widget assertion matchers keyed by name, suitable for passing to `expect.extend`. */
|
|
74
|
+
export const matchers = {
|
|
75
|
+
toHaveDisplayValue,
|
|
76
|
+
toHaveTextContent,
|
|
77
|
+
toHaveAccessibleName,
|
|
78
|
+
toHavePlaceholderText,
|
|
79
|
+
toBeChecked,
|
|
80
|
+
toBePressed,
|
|
81
|
+
toBeExpanded,
|
|
82
|
+
toBeSelected,
|
|
83
|
+
toHaveValue,
|
|
84
|
+
};
|
|
85
|
+
const globalExpect = () => {
|
|
86
|
+
const candidate = Reflect.get(globalThis, "expect");
|
|
87
|
+
if (candidate && typeof candidate.extend === "function") {
|
|
88
|
+
return candidate;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
};
|
|
92
|
+
let registered = false;
|
|
93
|
+
/** Registers the widget matchers on the global `expect`, when one is available. Safe to call more than once. */
|
|
94
|
+
export const registerMatchers = () => {
|
|
95
|
+
if (registered)
|
|
96
|
+
return;
|
|
97
|
+
const expect = globalExpect();
|
|
98
|
+
if (!expect)
|
|
99
|
+
return;
|
|
100
|
+
expect.extend(matchers);
|
|
101
|
+
registered = true;
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../src/matchers.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"@vitest/expect\" />\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport {\n getWidgetAccessibleName,\n getWidgetCheckedState,\n getWidgetDisplayValue,\n getWidgetExpandedState,\n getWidgetNodeText,\n getWidgetPlaceholderText,\n getWidgetPressedState,\n getWidgetSelectedState,\n getWidgetValue,\n} from \"./widget-accessible-properties.js\";\n\n/** The expected value for a text matcher: an exact string or a regular expression. */\nexport type TextExpectation = string | RegExp;\n\ntype MatcherResult = { pass: boolean; message: () => string };\n\ninterface MatcherContext {\n isNot: boolean;\n}\n\ntype TextMatcher = (this: MatcherContext, received: unknown, expected?: TextExpectation) => MatcherResult;\ntype StateMatcher = (this: MatcherContext, received: unknown) => MatcherResult;\ntype ValueMatcher = (this: MatcherContext, received: unknown, expected: number) => MatcherResult;\n\nconst asWidget = (received: unknown, matcherName: string): Gtk.Widget => {\n if (!(received instanceof Gtk.Widget)) {\n throw new TypeError(`${matcherName}: received value must be a Gtk.Widget, got ${typeof received}`);\n }\n return received;\n};\n\nconst describeWidget = (widget: Gtk.Widget): string => {\n const role = Gtk.AccessibleRole[widget.getAccessibleRole()];\n const name = getWidgetAccessibleName(widget);\n return name !== null ? `<${role} name=${JSON.stringify(name)}>` : `<${role}>`;\n};\n\nconst matchesText = (actual: string, expected: TextExpectation, mode: \"exact\" | \"substring\"): boolean => {\n if (expected instanceof RegExp) {\n expected.lastIndex = 0;\n return expected.test(actual);\n }\n return mode === \"exact\" ? actual === expected : actual.includes(expected);\n};\n\nconst describeExpected = (expected: TextExpectation): string =>\n expected instanceof RegExp ? String(expected) : JSON.stringify(expected);\n\nconst textMatcher = (\n matcherName: string,\n read: (widget: Gtk.Widget) => string | null,\n mode: \"exact\" | \"substring\",\n): TextMatcher =>\n function (this: MatcherContext, received: unknown, expected?: TextExpectation): MatcherResult {\n const widget = asWidget(received, matcherName);\n const actual = read(widget);\n\n if (expected === undefined) {\n const pass = actual !== null && actual !== \"\";\n return {\n pass,\n message: () =>\n `expected widget ${this.isNot ? \"not \" : \"\"}to have a non-empty value for ${matcherName}, ` +\n `but got ${JSON.stringify(actual)}\\n${describeWidget(widget)}`,\n };\n }\n\n const pass = actual !== null && matchesText(actual, expected, mode);\n return {\n pass,\n message: () =>\n `expected widget ${this.isNot ? \"not \" : \"\"}${matcherName} ${describeExpected(expected)}, ` +\n `but received ${JSON.stringify(actual)}\\n${describeWidget(widget)}`,\n };\n };\n\nconst booleanStateMatcher = (\n matcherName: string,\n stateName: string,\n read: (widget: Gtk.Widget) => boolean | null,\n): StateMatcher =>\n function (this: MatcherContext, received: unknown): MatcherResult {\n const widget = asWidget(received, matcherName);\n const state = read(widget);\n if (state === null) {\n throw new Error(\n `${matcherName}: widget does not expose a ${stateName} state ` +\n `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\\n${describeWidget(widget)}`,\n );\n }\n return {\n pass: state,\n message: () => `expected widget ${this.isNot ? \"not \" : \"\"}to be ${stateName}\\n${describeWidget(widget)}`,\n };\n };\n\nexport const toHaveDisplayValue: TextMatcher = textMatcher(\"toHaveDisplayValue\", getWidgetDisplayValue, \"exact\");\nexport const toHaveTextContent: TextMatcher = textMatcher(\n \"toHaveTextContent\",\n (widget) => getWidgetNodeText(widget) ?? getWidgetAccessibleName(widget),\n \"substring\",\n);\nexport const toHaveAccessibleName: TextMatcher = textMatcher(\"toHaveAccessibleName\", getWidgetAccessibleName, \"exact\");\nexport const toHavePlaceholderText: TextMatcher = textMatcher(\n \"toHavePlaceholderText\",\n getWidgetPlaceholderText,\n \"exact\",\n);\n\nexport const toBeChecked: StateMatcher = booleanStateMatcher(\"toBeChecked\", \"checked\", getWidgetCheckedState);\nexport const toBePressed: StateMatcher = booleanStateMatcher(\"toBePressed\", \"pressed\", getWidgetPressedState);\nexport const toBeExpanded: StateMatcher = booleanStateMatcher(\"toBeExpanded\", \"expanded\", getWidgetExpandedState);\nexport const toBeSelected: StateMatcher = booleanStateMatcher(\"toBeSelected\", \"selected\", getWidgetSelectedState);\n\nexport const toHaveValue: ValueMatcher = function (\n this: MatcherContext,\n received: unknown,\n expected: number,\n): MatcherResult {\n const widget = asWidget(received, \"toHaveValue\");\n const actual = getWidgetValue(widget).now;\n if (actual === null) {\n throw new Error(\n `toHaveValue: widget does not expose a numeric value ` +\n `(role ${Gtk.AccessibleRole[widget.getAccessibleRole()]})\\n${describeWidget(widget)}`,\n );\n }\n return {\n pass: actual === expected,\n message: () =>\n `expected widget ${this.isNot ? \"not \" : \"\"}to have value ${expected}, but received ${actual}\\n${describeWidget(widget)}`,\n };\n};\n\ninterface MatcherImplementations {\n toHaveDisplayValue: TextMatcher;\n toHaveTextContent: TextMatcher;\n toHaveAccessibleName: TextMatcher;\n toHavePlaceholderText: TextMatcher;\n toBeChecked: StateMatcher;\n toBePressed: StateMatcher;\n toBeExpanded: StateMatcher;\n toBeSelected: StateMatcher;\n toHaveValue: ValueMatcher;\n}\n\n/** The widget assertion matchers keyed by name, suitable for passing to `expect.extend`. */\nexport const matchers: MatcherImplementations = {\n toHaveDisplayValue,\n toHaveTextContent,\n toHaveAccessibleName,\n toHavePlaceholderText,\n toBeChecked,\n toBePressed,\n toBeExpanded,\n toBeSelected,\n toHaveValue,\n};\n\ntype ExpectExtend = { extend: (m: MatcherImplementations) => void };\n\nconst globalExpect = (): ExpectExtend | null => {\n const candidate: unknown = Reflect.get(globalThis, \"expect\");\n if (candidate && typeof (candidate as ExpectExtend).extend === \"function\") {\n return candidate as ExpectExtend;\n }\n return null;\n};\n\nlet registered = false;\n\n/** Registers the widget matchers on the global `expect`, when one is available. Safe to call more than once. */\nexport const registerMatchers = (): void => {\n if (registered) return;\n const expect = globalExpect();\n if (!expect) return;\n expect.extend(matchers);\n registered = true;\n};\n\ninterface WidgetMatchers<R = void> {\n toHaveDisplayValue(expected?: TextExpectation): R;\n toHaveTextContent(expected?: TextExpectation): R;\n toHaveAccessibleName(expected?: TextExpectation): R;\n toHavePlaceholderText(expected?: TextExpectation): R;\n toBeChecked(): R;\n toBePressed(): R;\n toBeExpanded(): R;\n toBeSelected(): R;\n toHaveValue(expected: number): R;\n}\n\ndeclare module \"@vitest/expect\" {\n // biome-ignore lint/suspicious/noExplicitAny: must match the `Assertion<T>` signature it augments for declaration merging\n interface Assertion<T = any> extends WidgetMatchers {}\n interface AsymmetricMatchersContaining extends WidgetMatchers {}\n}\n"]}
|
package/dist/pretty-widget.d.ts
CHANGED
|
@@ -1,37 +1,33 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
1
2
|
import { type Container } from "./traversal.js";
|
|
3
|
+
type WidgetIdResolver = (widget: Gtk.Widget) => string;
|
|
2
4
|
/**
|
|
3
|
-
* Options
|
|
5
|
+
* Options controlling how a widget tree is rendered to a string by
|
|
6
|
+
* {@link prettyWidget} and {@link logWidget}.
|
|
4
7
|
*/
|
|
5
8
|
export type PrettyWidgetOptions = {
|
|
6
|
-
/**
|
|
9
|
+
/** Truncates the output once it exceeds this many characters. */
|
|
7
10
|
maxLength?: number;
|
|
8
|
-
/**
|
|
11
|
+
/** Whether to apply ANSI color highlighting; defaults to the terminal capabilities. */
|
|
9
12
|
highlight?: boolean;
|
|
10
|
-
/**
|
|
11
|
-
|
|
13
|
+
/** Resolves an `id` attribute to show for each widget. */
|
|
14
|
+
getId?: WidgetIdResolver;
|
|
12
15
|
};
|
|
13
16
|
/**
|
|
14
|
-
*
|
|
17
|
+
* Renders a widget tree as an indented, HTML-like string showing each widget's
|
|
18
|
+
* tag, accessible attributes, and text content.
|
|
15
19
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param container - The container widget or application to format
|
|
20
|
-
* @param options - Formatting options for length and highlighting
|
|
21
|
-
* @returns Formatted string representation of the widget tree
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```tsx
|
|
25
|
-
* import { prettyWidget } from "@gtkx/testing";
|
|
26
|
-
*
|
|
27
|
-
* console.log(prettyWidget(application));
|
|
28
|
-
* // Output:
|
|
29
|
-
* // <GtkApplicationWindow role="window">
|
|
30
|
-
* // <GtkButton role="button">
|
|
31
|
-
* // Click me
|
|
32
|
-
* // </GtkButton>
|
|
33
|
-
* // </GtkApplicationWindow>
|
|
34
|
-
* ```
|
|
20
|
+
* @param container The scope whose widget tree is formatted.
|
|
21
|
+
* @param options Formatting options such as truncation length and highlighting.
|
|
22
|
+
* @returns The formatted representation of the tree.
|
|
35
23
|
*/
|
|
36
24
|
export declare const prettyWidget: (container: Container, options?: PrettyWidgetOptions) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Prints one or more widget trees to the console using {@link prettyWidget}.
|
|
27
|
+
*
|
|
28
|
+
* @param container A single scope or an array of scopes to format and print.
|
|
29
|
+
* @param options Formatting options passed through to {@link prettyWidget}.
|
|
30
|
+
*/
|
|
31
|
+
export declare const logWidget: (container: Container | Container[], options?: PrettyWidgetOptions) => void;
|
|
32
|
+
export {};
|
|
37
33
|
//# sourceMappingURL=pretty-widget.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pretty-widget.d.ts","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pretty-widget.d.ts","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,KAAK,SAAS,EAAS,MAAM,gBAAgB,CAAC;AAMvD,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC;AAEvD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAqGF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,cAAe,SAAS,YAAW,mBAAmB,KAAQ,MAqBtF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,cAAe,SAAS,GAAG,SAAS,EAAE,YAAY,mBAAmB,KAAG,IAK7F,CAAC"}
|
package/dist/pretty-widget.js
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { sortStringsBy } from "@gtkx/utils";
|
|
2
2
|
import { formatRole } from "./role-helpers.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { roots } from "./traversal.js";
|
|
4
|
+
import { getWidgetNodeText } from "./widget-accessible-properties.js";
|
|
5
5
|
const DEFAULT_MAX_LENGTH = 7000;
|
|
6
6
|
const INDENT = " ";
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const buildAttrs = (widget, getId) => {
|
|
8
|
+
const attrs = [];
|
|
9
|
+
if (getId) {
|
|
10
|
+
attrs.push(["id", getId(widget)]);
|
|
11
|
+
}
|
|
12
|
+
const name = widget.getName();
|
|
13
|
+
if (name) {
|
|
14
|
+
attrs.push(["name", name]);
|
|
15
|
+
}
|
|
16
|
+
attrs.push(["role", formatRole(widget.getAccessibleRole())]);
|
|
17
|
+
if (!widget.getSensitive()) {
|
|
18
|
+
attrs.push(["accessible-disabled", "true"]);
|
|
19
|
+
}
|
|
20
|
+
if (!widget.getVisible()) {
|
|
21
|
+
attrs.push(["accessible-hidden", "true"]);
|
|
14
22
|
}
|
|
15
|
-
|
|
23
|
+
const idAttrs = attrs.filter(([key]) => key === "id");
|
|
24
|
+
const otherAttrs = sortStringsBy(attrs.filter(([key]) => key !== "id"), ([key]) => key);
|
|
25
|
+
return [...idAttrs, ...otherAttrs];
|
|
26
|
+
};
|
|
27
|
+
const ansi = {
|
|
28
|
+
cyan: "\x1b[36m",
|
|
29
|
+
yellow: "\x1b[33m",
|
|
30
|
+
green: "\x1b[32m",
|
|
31
|
+
reset: "\x1b[0m",
|
|
16
32
|
};
|
|
17
33
|
const shouldHighlight = () => {
|
|
18
34
|
if (typeof process === "undefined")
|
|
@@ -21,141 +37,79 @@ const shouldHighlight = () => {
|
|
|
21
37
|
return false;
|
|
22
38
|
if (process.env.COLORS === "true" || process.env.FORCE_COLOR)
|
|
23
39
|
return true;
|
|
24
|
-
return process.stdout
|
|
25
|
-
};
|
|
26
|
-
const ansi = {
|
|
27
|
-
cyan: "\x1b[36m",
|
|
28
|
-
yellow: "\x1b[33m",
|
|
29
|
-
green: "\x1b[32m",
|
|
30
|
-
reset: "\x1b[0m",
|
|
40
|
+
return process.stdout.isTTY;
|
|
31
41
|
};
|
|
32
42
|
const createColors = (enabled) => {
|
|
33
43
|
if (!enabled) {
|
|
34
44
|
const identity = (s) => s;
|
|
35
|
-
return { tag: identity, attr: identity, value: identity
|
|
45
|
+
return { tag: identity, attr: identity, value: identity };
|
|
36
46
|
}
|
|
37
47
|
return {
|
|
38
48
|
tag: (s) => `${ansi.cyan}${s}${ansi.reset}`,
|
|
39
49
|
attr: (s) => `${ansi.yellow}${s}${ansi.reset}`,
|
|
40
50
|
value: (s) => `${ansi.green}${s}${ansi.reset}`,
|
|
41
|
-
text: (s) => s,
|
|
42
|
-
reset: ansi.reset,
|
|
43
51
|
};
|
|
44
52
|
};
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const escapeAttrValue = (value) => {
|
|
49
|
-
return value.replace(/"/g, """);
|
|
50
|
-
};
|
|
51
|
-
const formatAttributes = (widget, colors, includeIds) => {
|
|
52
|
-
const attrs = [];
|
|
53
|
-
if (includeIds) {
|
|
54
|
-
attrs.push(["id", getWidgetDebugId(widget)]);
|
|
55
|
-
}
|
|
56
|
-
const name = widget.getName();
|
|
57
|
-
if (name) {
|
|
58
|
-
attrs.push(["data-testid", name]);
|
|
59
|
-
}
|
|
60
|
-
const role = widget.getAccessibleRole();
|
|
61
|
-
if (role !== undefined) {
|
|
62
|
-
attrs.push(["role", formatRole(role)]);
|
|
63
|
-
}
|
|
64
|
-
if (!widget.getSensitive()) {
|
|
65
|
-
attrs.push(["aria-disabled", "true"]);
|
|
66
|
-
}
|
|
67
|
-
if (!widget.getVisible()) {
|
|
68
|
-
attrs.push(["aria-hidden", "true"]);
|
|
69
|
-
}
|
|
70
|
-
if (attrs.length === 0)
|
|
71
|
-
return "";
|
|
72
|
-
return attrs
|
|
73
|
-
.sort(([a], [b]) => {
|
|
74
|
-
if (a === "id")
|
|
75
|
-
return -1;
|
|
76
|
-
if (b === "id")
|
|
77
|
-
return 1;
|
|
78
|
-
return a.localeCompare(b);
|
|
79
|
-
})
|
|
80
|
-
.map(([key, value]) => ` ${colors.attr(key)}=${colors.value(`"${escapeAttrValue(value)}"`)}`)
|
|
81
|
-
.join("");
|
|
82
|
-
};
|
|
83
|
-
const hasChildren = (widget) => {
|
|
84
|
-
return widget.getFirstChild() !== null;
|
|
85
|
-
};
|
|
86
|
-
const printWidget = (widget, colors, depth, includeIds) => {
|
|
53
|
+
const escapeAttrValue = (value) => value.replaceAll('"', """);
|
|
54
|
+
const formatAttrs = (attrs, colors) => attrs.map(([key, value]) => ` ${colors.attr(key)}=${colors.value(`"${escapeAttrValue(value)}"`)}`).join("");
|
|
55
|
+
const formatWidget = (widget, depth, getId, colors) => {
|
|
87
56
|
const indent = INDENT.repeat(depth);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
child = child.getNextSibling();
|
|
96
|
-
}
|
|
97
|
-
const openTag = `${colors.tag("<")}${colors.tag(tagName)}${attributes}${colors.tag(">")}`;
|
|
98
|
-
if (!hasChildren(widget) && !text) {
|
|
57
|
+
const tag = widget.constructor.name;
|
|
58
|
+
const attrs = formatAttrs(buildAttrs(widget, getId), colors);
|
|
59
|
+
const openTag = `${colors.tag("<")}${colors.tag(tag)}${attrs}${colors.tag(">")}`;
|
|
60
|
+
const closeTag = `${colors.tag("</")}${colors.tag(tag)}${colors.tag(">")}`;
|
|
61
|
+
const text = getWidgetNodeText(widget);
|
|
62
|
+
const firstChild = widget.getFirstChild();
|
|
63
|
+
if (!text && !firstChild) {
|
|
99
64
|
return `${indent}${openTag}\n`;
|
|
100
65
|
}
|
|
101
|
-
|
|
102
|
-
if (text && !hasChildren(widget)) {
|
|
103
|
-
const textContent = colors.text(text);
|
|
104
|
-
return `${indent}${openTag}\n${indent}${INDENT}${textContent}\n${indent}${closeTag}\n`;
|
|
105
|
-
}
|
|
106
|
-
let result = `${indent}${openTag}\n`;
|
|
66
|
+
let output = `${indent}${openTag}\n`;
|
|
107
67
|
if (text) {
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
for (const childOutput of children) {
|
|
111
|
-
result += childOutput;
|
|
68
|
+
output += `${indent}${INDENT}${text}\n`;
|
|
112
69
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (isApplication(container)) {
|
|
118
|
-
const windows = Gtk.Window.listToplevels();
|
|
119
|
-
return windows.map((window) => printWidget(window, colors, 0, includeIds)).join("");
|
|
70
|
+
let child = firstChild;
|
|
71
|
+
while (child) {
|
|
72
|
+
output += formatWidget(child, depth + 1, getId, colors);
|
|
73
|
+
child = child.getNextSibling();
|
|
120
74
|
}
|
|
121
|
-
|
|
75
|
+
output += `${indent}${closeTag}\n`;
|
|
76
|
+
return output;
|
|
122
77
|
};
|
|
123
78
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* Renders the widget hierarchy in an HTML-like format with accessibility
|
|
127
|
-
* attributes like role, data-testid, and text content.
|
|
128
|
-
*
|
|
129
|
-
* @param container - The container widget or application to format
|
|
130
|
-
* @param options - Formatting options for length and highlighting
|
|
131
|
-
* @returns Formatted string representation of the widget tree
|
|
79
|
+
* Renders a widget tree as an indented, HTML-like string showing each widget's
|
|
80
|
+
* tag, accessible attributes, and text content.
|
|
132
81
|
*
|
|
133
|
-
* @
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* console.log(prettyWidget(application));
|
|
138
|
-
* // Output:
|
|
139
|
-
* // <GtkApplicationWindow role="window">
|
|
140
|
-
* // <GtkButton role="button">
|
|
141
|
-
* // Click me
|
|
142
|
-
* // </GtkButton>
|
|
143
|
-
* // </GtkApplicationWindow>
|
|
144
|
-
* ```
|
|
82
|
+
* @param container The scope whose widget tree is formatted.
|
|
83
|
+
* @param options Formatting options such as truncation length and highlighting.
|
|
84
|
+
* @returns The formatted representation of the tree.
|
|
145
85
|
*/
|
|
146
86
|
export const prettyWidget = (container, options = {}) => {
|
|
147
87
|
const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;
|
|
148
88
|
const maxLength = options.maxLength ?? envLimit;
|
|
149
|
-
const highlight = options.highlight ?? shouldHighlight();
|
|
150
|
-
const includeIds = options.includeIds ?? false;
|
|
151
89
|
if (maxLength === 0) {
|
|
152
90
|
return "";
|
|
153
91
|
}
|
|
92
|
+
const highlight = options.highlight ?? shouldHighlight();
|
|
154
93
|
const colors = createColors(highlight);
|
|
155
|
-
|
|
94
|
+
let output = "";
|
|
95
|
+
for (const root of roots(container)) {
|
|
96
|
+
output += formatWidget(root, 0, options.getId, colors);
|
|
97
|
+
}
|
|
156
98
|
if (output.length > maxLength) {
|
|
157
99
|
return `${output.slice(0, maxLength)}...`;
|
|
158
100
|
}
|
|
159
101
|
return output.trimEnd();
|
|
160
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* Prints one or more widget trees to the console using {@link prettyWidget}.
|
|
105
|
+
*
|
|
106
|
+
* @param container A single scope or an array of scopes to format and print.
|
|
107
|
+
* @param options Formatting options passed through to {@link prettyWidget}.
|
|
108
|
+
*/
|
|
109
|
+
export const logWidget = (container, options) => {
|
|
110
|
+
const containers = Array.isArray(container) ? container : [container];
|
|
111
|
+
for (const target of containers) {
|
|
112
|
+
console.log(prettyWidget(target, options));
|
|
113
|
+
}
|
|
114
|
+
};
|
|
161
115
|
//# sourceMappingURL=pretty-widget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pretty-widget.js","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pretty-widget.js","sourceRoot":"","sources":["../src/pretty-widget.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAkB,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,MAAM,GAAG,IAAI,CAAC;AAiBpB,MAAM,UAAU,GAAG,CAAC,MAAkB,EAAE,KAAmC,EAAsB,EAAE;IAC/F,MAAM,KAAK,GAAuB,EAAE,CAAC;IAErC,IAAI,KAAK,EAAE,CAAC;QACR,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,IAAI,IAAI,EAAE,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;IAE7D,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,aAAa,CAC5B,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,EACrC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CACjB,CAAC;IACF,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;AACvC,CAAC,CAAC;AAQF,MAAM,IAAI,GAAG;IACT,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,SAAS;CACnB,CAAC;AAEF,MAAM,eAAe,GAAG,GAAY,EAAE;IAClC,IAAI,OAAO,OAAO,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IACzE,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC1E,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;AAChC,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAU,EAAE;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC9D,CAAC;IACD,OAAO;QACH,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;QAC3C,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;QAC9C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE;KACjD,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAEnF,MAAM,WAAW,GAAG,CAAC,KAAyB,EAAE,MAAc,EAAU,EAAE,CACtE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEhH,MAAM,YAAY,GAAG,CACjB,MAAkB,EAClB,KAAa,EACb,KAAmC,EACnC,MAAc,EACR,EAAE;IACR,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IACjF,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IAE3E,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;IAE1C,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC;IACnC,CAAC;IAED,IAAI,MAAM,GAAG,GAAG,MAAM,GAAG,OAAO,IAAI,CAAC;IACrC,IAAI,IAAI,EAAE,CAAC;QACP,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,IAAI,CAAC;IAC5C,CAAC;IACD,IAAI,KAAK,GAAG,UAAU,CAAC;IACvB,OAAO,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACxD,KAAK,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;IACnC,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,GAAG,QAAQ,IAAI,CAAC;IACnC,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAoB,EAAE,OAAO,GAAwB,EAAE,EAAU,EAAE;IAC5F,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC;IAC5G,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC;IAEhD,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,eAAe,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAEvC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC5B,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC;IAC9C,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC5B,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAAkC,EAAE,OAA6B,EAAQ,EAAE;IACjG,MAAM,UAAU,GAAgB,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACnF,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,CAAC;AACL,CAAC,CAAC","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport { sortStringsBy } from \"@gtkx/utils\";\nimport { formatRole } from \"./role-helpers.js\";\nimport { type Container, roots } from \"./traversal.js\";\nimport { getWidgetNodeText } from \"./widget-accessible-properties.js\";\n\nconst DEFAULT_MAX_LENGTH = 7000;\nconst INDENT = \" \";\n\ntype WidgetIdResolver = (widget: Gtk.Widget) => string;\n\n/**\n * Options controlling how a widget tree is rendered to a string by\n * {@link prettyWidget} and {@link logWidget}.\n */\nexport type PrettyWidgetOptions = {\n /** Truncates the output once it exceeds this many characters. */\n maxLength?: number;\n /** Whether to apply ANSI color highlighting; defaults to the terminal capabilities. */\n highlight?: boolean;\n /** Resolves an `id` attribute to show for each widget. */\n getId?: WidgetIdResolver;\n};\n\nconst buildAttrs = (widget: Gtk.Widget, getId: WidgetIdResolver | undefined): [string, string][] => {\n const attrs: [string, string][] = [];\n\n if (getId) {\n attrs.push([\"id\", getId(widget)]);\n }\n\n const name = widget.getName();\n if (name) {\n attrs.push([\"name\", name]);\n }\n\n attrs.push([\"role\", formatRole(widget.getAccessibleRole())]);\n\n if (!widget.getSensitive()) {\n attrs.push([\"accessible-disabled\", \"true\"]);\n }\n\n if (!widget.getVisible()) {\n attrs.push([\"accessible-hidden\", \"true\"]);\n }\n\n const idAttrs = attrs.filter(([key]) => key === \"id\");\n const otherAttrs = sortStringsBy(\n attrs.filter(([key]) => key !== \"id\"),\n ([key]) => key,\n );\n return [...idAttrs, ...otherAttrs];\n};\n\ntype Colors = {\n tag: (s: string) => string;\n attr: (s: string) => string;\n value: (s: string) => string;\n};\n\nconst ansi = {\n cyan: \"\\x1b[36m\",\n yellow: \"\\x1b[33m\",\n green: \"\\x1b[32m\",\n reset: \"\\x1b[0m\",\n};\n\nconst shouldHighlight = (): boolean => {\n if (typeof process === \"undefined\") return false;\n if (process.env.COLORS === \"false\" || process.env.NO_COLOR) return false;\n if (process.env.COLORS === \"true\" || process.env.FORCE_COLOR) return true;\n return process.stdout.isTTY;\n};\n\nconst createColors = (enabled: boolean): Colors => {\n if (!enabled) {\n const identity = (s: string): string => s;\n return { tag: identity, attr: identity, value: identity };\n }\n return {\n tag: (s) => `${ansi.cyan}${s}${ansi.reset}`,\n attr: (s) => `${ansi.yellow}${s}${ansi.reset}`,\n value: (s) => `${ansi.green}${s}${ansi.reset}`,\n };\n};\n\nconst escapeAttrValue = (value: string): string => value.replaceAll('\"', \""\");\n\nconst formatAttrs = (attrs: [string, string][], colors: Colors): string =>\n attrs.map(([key, value]) => ` ${colors.attr(key)}=${colors.value(`\"${escapeAttrValue(value)}\"`)}`).join(\"\");\n\nconst formatWidget = (\n widget: Gtk.Widget,\n depth: number,\n getId: WidgetIdResolver | undefined,\n colors: Colors,\n): string => {\n const indent = INDENT.repeat(depth);\n const tag = widget.constructor.name;\n const attrs = formatAttrs(buildAttrs(widget, getId), colors);\n const openTag = `${colors.tag(\"<\")}${colors.tag(tag)}${attrs}${colors.tag(\">\")}`;\n const closeTag = `${colors.tag(\"</\")}${colors.tag(tag)}${colors.tag(\">\")}`;\n\n const text = getWidgetNodeText(widget);\n const firstChild = widget.getFirstChild();\n\n if (!text && !firstChild) {\n return `${indent}${openTag}\\n`;\n }\n\n let output = `${indent}${openTag}\\n`;\n if (text) {\n output += `${indent}${INDENT}${text}\\n`;\n }\n let child = firstChild;\n while (child) {\n output += formatWidget(child, depth + 1, getId, colors);\n child = child.getNextSibling();\n }\n output += `${indent}${closeTag}\\n`;\n return output;\n};\n\n/**\n * Renders a widget tree as an indented, HTML-like string showing each widget's\n * tag, accessible attributes, and text content.\n *\n * @param container The scope whose widget tree is formatted.\n * @param options Formatting options such as truncation length and highlighting.\n * @returns The formatted representation of the tree.\n */\nexport const prettyWidget = (container: Container, options: PrettyWidgetOptions = {}): string => {\n const envLimit = process.env.DEBUG_PRINT_LIMIT ? Number(process.env.DEBUG_PRINT_LIMIT) : DEFAULT_MAX_LENGTH;\n const maxLength = options.maxLength ?? envLimit;\n\n if (maxLength === 0) {\n return \"\";\n }\n\n const highlight = options.highlight ?? shouldHighlight();\n const colors = createColors(highlight);\n\n let output = \"\";\n for (const root of roots(container)) {\n output += formatWidget(root, 0, options.getId, colors);\n }\n\n if (output.length > maxLength) {\n return `${output.slice(0, maxLength)}...`;\n }\n\n return output.trimEnd();\n};\n\n/**\n * Prints one or more widget trees to the console using {@link prettyWidget}.\n *\n * @param container A single scope or an array of scopes to format and print.\n * @param options Formatting options passed through to {@link prettyWidget}.\n */\nexport const logWidget = (container: Container | Container[], options?: PrettyWidgetOptions): void => {\n const containers: Container[] = Array.isArray(container) ? container : [container];\n for (const target of containers) {\n console.log(prettyWidget(target, options));\n }\n};\n"]}
|