@gtkx/testing 1.0.0 → 1.2.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.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/dist/act.d.ts +1 -3
  3. package/dist/act.d.ts.map +1 -1
  4. package/dist/act.js +1 -1
  5. package/dist/act.js.map +1 -1
  6. package/dist/build-queries.d.ts +1 -1
  7. package/dist/build-queries.js +1 -1
  8. package/dist/build-queries.js.map +1 -1
  9. package/dist/config.d.ts +8 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +1 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/frame-sync.d.ts +2 -2
  14. package/dist/frame-sync.d.ts.map +1 -1
  15. package/dist/frame-sync.js +10 -10
  16. package/dist/frame-sync.js.map +1 -1
  17. package/dist/harness-window.d.ts +2 -1
  18. package/dist/harness-window.d.ts.map +1 -1
  19. package/dist/harness-window.js +12 -1
  20. package/dist/harness-window.js.map +1 -1
  21. package/dist/internal.d.ts +1 -0
  22. package/dist/internal.d.ts.map +1 -1
  23. package/dist/internal.js +1 -0
  24. package/dist/internal.js.map +1 -1
  25. package/dist/matchers.d.ts.map +1 -1
  26. package/dist/matchers.js +22 -2
  27. package/dist/matchers.js.map +1 -1
  28. package/dist/pretty-widget.d.ts.map +1 -1
  29. package/dist/pretty-widget.js +2 -1
  30. package/dist/pretty-widget.js.map +1 -1
  31. package/dist/production.d.ts.map +1 -1
  32. package/dist/production.js +2 -2
  33. package/dist/production.js.map +1 -1
  34. package/dist/render.d.ts +12 -1
  35. package/dist/render.d.ts.map +1 -1
  36. package/dist/render.js +45 -11
  37. package/dist/render.js.map +1 -1
  38. package/dist/role-helpers.d.ts.map +1 -1
  39. package/dist/role-helpers.js +2 -1
  40. package/dist/role-helpers.js.map +1 -1
  41. package/dist/screenshot.d.ts.map +1 -1
  42. package/dist/screenshot.js +8 -14
  43. package/dist/screenshot.js.map +1 -1
  44. package/dist/timers.d.ts +9 -0
  45. package/dist/timers.d.ts.map +1 -0
  46. package/dist/timers.js +21 -0
  47. package/dist/timers.js.map +1 -0
  48. package/dist/traversal.d.ts +4 -1
  49. package/dist/traversal.d.ts.map +1 -1
  50. package/dist/traversal.js +16 -1
  51. package/dist/traversal.js.map +1 -1
  52. package/dist/types.d.ts +6 -1
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js.map +1 -1
  55. package/dist/user-event/adjustment.d.ts.map +1 -1
  56. package/dist/user-event/adjustment.js +2 -1
  57. package/dist/user-event/adjustment.js.map +1 -1
  58. package/dist/user-event/click.d.ts +5 -4
  59. package/dist/user-event/click.d.ts.map +1 -1
  60. package/dist/user-event/click.js +5 -4
  61. package/dist/user-event/click.js.map +1 -1
  62. package/dist/user-event/column-header-click.d.ts.map +1 -1
  63. package/dist/user-event/column-header-click.js +2 -12
  64. package/dist/user-event/column-header-click.js.map +1 -1
  65. package/dist/user-event/event-wrapper.d.ts.map +1 -1
  66. package/dist/user-event/event-wrapper.js +34 -21
  67. package/dist/user-event/event-wrapper.js.map +1 -1
  68. package/dist/user-event/index.d.ts +36 -10
  69. package/dist/user-event/index.d.ts.map +1 -1
  70. package/dist/user-event/index.js +4 -3
  71. package/dist/user-event/index.js.map +1 -1
  72. package/dist/user-event/keyboard.d.ts.map +1 -1
  73. package/dist/user-event/keyboard.js +93 -38
  74. package/dist/user-event/keyboard.js.map +1 -1
  75. package/dist/user-event/list-row-click.d.ts.map +1 -1
  76. package/dist/user-event/list-row-click.js +4 -5
  77. package/dist/user-event/list-row-click.js.map +1 -1
  78. package/dist/user-event/native-click.d.ts.map +1 -1
  79. package/dist/user-event/native-click.js +4 -0
  80. package/dist/user-event/native-click.js.map +1 -1
  81. package/dist/user-event/notebook-tab-click.d.ts +5 -0
  82. package/dist/user-event/notebook-tab-click.d.ts.map +1 -0
  83. package/dist/user-event/notebook-tab-click.js +39 -0
  84. package/dist/user-event/notebook-tab-click.js.map +1 -0
  85. package/dist/user-event/text.d.ts +6 -3
  86. package/dist/user-event/text.d.ts.map +1 -1
  87. package/dist/user-event/text.js +88 -33
  88. package/dist/user-event/text.js.map +1 -1
  89. package/dist/wait-for.d.ts.map +1 -1
  90. package/dist/wait-for.js +4 -3
  91. package/dist/wait-for.js.map +1 -1
  92. package/dist/widget-getters.d.ts +4 -1
  93. package/dist/widget-getters.d.ts.map +1 -1
  94. package/dist/widget-getters.js +29 -2
  95. package/dist/widget-getters.js.map +1 -1
  96. package/dist/window-state.d.ts +10 -0
  97. package/dist/window-state.d.ts.map +1 -0
  98. package/dist/window-state.js +41 -0
  99. package/dist/window-state.js.map +1 -0
  100. package/package.json +15 -7
  101. package/src/act.ts +1 -1
  102. package/src/build-queries.ts +1 -1
  103. package/src/config.ts +10 -1
  104. package/src/frame-sync.ts +15 -10
  105. package/src/harness-window.ts +15 -1
  106. package/src/internal.ts +1 -0
  107. package/src/matchers.ts +29 -2
  108. package/src/pretty-widget.ts +2 -1
  109. package/src/production.ts +2 -3
  110. package/src/render.tsx +67 -12
  111. package/src/role-helpers.ts +2 -1
  112. package/src/screenshot.ts +10 -17
  113. package/src/timers.ts +31 -0
  114. package/src/traversal.ts +22 -0
  115. package/src/types.ts +6 -1
  116. package/src/user-event/adjustment.ts +2 -1
  117. package/src/user-event/click.ts +5 -4
  118. package/src/user-event/column-header-click.ts +2 -16
  119. package/src/user-event/event-wrapper.ts +41 -25
  120. package/src/user-event/index.ts +40 -13
  121. package/src/user-event/keyboard.ts +124 -54
  122. package/src/user-event/list-row-click.ts +4 -7
  123. package/src/user-event/native-click.ts +5 -0
  124. package/src/user-event/notebook-tab-click.ts +57 -0
  125. package/src/user-event/text.ts +113 -38
  126. package/src/wait-for.ts +4 -4
  127. package/src/widget-getters.ts +55 -2
  128. package/src/window-state.ts +70 -0
@@ -8,27 +8,93 @@ import { callBooleanGetter } from "../widget-getters.js";
8
8
  import { wrapEvent } from "./event-wrapper.js";
9
9
  const EDITABLE_REQUIRED = `expected editable widget (${formatRoleList(EDITABLE_ROLES)})`;
10
10
  const CLEAR_REFUSED = "Cannot clear element: the widget refuses to delete part of its text";
11
- const insertEditableText = (widget, text) => {
11
+ const SELECTION_DELETE_COUNT = 1;
12
+ const SINGLE_CHARACTER_LENGTH = 1;
13
+ const TEXT_END_POSITION = -1;
14
+ const isWidgetEditable = (widget) => callBooleanGetter(widget, "getEditable") ?? true;
15
+ const readSelection = (widget) => getWidgetSelection(widget) ?? "";
16
+ const hasSelectedText = (widget) => widget.getSelectionBounds()[0];
17
+ const getDelegateText = (widget) => {
18
+ const delegate = getEditableDelegate(widget) ?? widget;
19
+ return delegate instanceof Gtk.Text ? delegate : null;
20
+ };
21
+ const getSelectionLength = (buffer) => {
22
+ const [isSelected, start, end] = buffer.getSelectionBounds();
23
+ return isSelected ? end.getOffset() - start.getOffset() : 0;
24
+ };
25
+ const deleteSelection = (widget) => {
12
26
  if (widget instanceof Gtk.TextView) {
13
- widget.emit("insert-at-cursor", text);
27
+ widget.getBuffer().deleteSelection(false, true);
14
28
  return;
15
29
  }
16
- const target = getEditableDelegate(widget) ?? widget;
17
- if (target instanceof Gtk.Text) {
18
- target.emit("insert-at-cursor", text);
30
+ widget.deleteSelection();
31
+ };
32
+ const focusPlainEditable = (widget) => {
33
+ const [isSelected, start, end] = widget.getSelectionBounds();
34
+ widget.grabFocus();
35
+ if (isSelected) {
36
+ widget.selectRegion(start, end);
37
+ return;
38
+ }
39
+ widget.setPosition(TEXT_END_POSITION);
40
+ };
41
+ const focusEditable = (widget) => {
42
+ if (widget instanceof Gtk.TextView) {
43
+ widget.grabFocus();
44
+ return;
45
+ }
46
+ const target = getDelegateText(widget);
47
+ if (target === null) {
48
+ focusPlainEditable(widget);
49
+ return;
50
+ }
51
+ target.grabFocusWithoutSelecting();
52
+ if (!hasSelectedText(widget)) {
53
+ widget.setPosition(TEXT_END_POSITION);
54
+ }
55
+ };
56
+ const insertTextViewText = (widget, text, kind) => {
57
+ const buffer = widget.getBuffer();
58
+ const length = getSelectionLength(buffer);
59
+ buffer.beginUserAction();
60
+ if (length > 0) {
61
+ widget.emit("delete-from-cursor", Gtk.DeleteType.CHARS, SELECTION_DELETE_COUNT);
62
+ if (kind === "typing" && length > SINGLE_CHARACTER_LENGTH) {
63
+ buffer.endUserAction();
64
+ buffer.beginUserAction();
65
+ }
66
+ }
67
+ widget.emit("insert-at-cursor", text);
68
+ buffer.endUserAction();
69
+ };
70
+ const insertDelegateText = (target, text) => {
71
+ if (hasSelectedText(target)) {
72
+ target.emit("delete-from-cursor", Gtk.DeleteType.CHARS, SELECTION_DELETE_COUNT);
73
+ }
74
+ target.emit("insert-at-cursor", text);
75
+ };
76
+ const insertPlainText = (widget, text) => {
77
+ if (!isWidgetEditable(widget)) {
19
78
  return;
20
79
  }
80
+ if (hasSelectedText(widget)) {
81
+ widget.deleteSelection();
82
+ }
21
83
  const position = widget.getPosition();
22
84
  const newPosition = widget.insertText(text, text.length, position);
23
85
  widget.setPosition(newPosition);
24
86
  };
25
- const readSelection = (widget) => getWidgetSelection(widget) ?? "";
26
- const deleteSelection = (widget) => {
87
+ const insertEditableText = (widget, text, kind) => {
27
88
  if (widget instanceof Gtk.TextView) {
28
- widget.getBuffer().deleteSelection(false, true);
89
+ insertTextViewText(widget, text, kind);
29
90
  return;
30
91
  }
31
- widget.deleteSelection();
92
+ const target = getDelegateText(widget);
93
+ if (target === null) {
94
+ insertPlainText(widget, text);
95
+ return;
96
+ }
97
+ insertDelegateText(target, text);
32
98
  };
33
99
  const deleteAllText = (widget) => {
34
100
  if (widget instanceof Gtk.TextView) {
@@ -38,20 +104,6 @@ const deleteAllText = (widget) => {
38
104
  }
39
105
  widget.deleteText(0, -1);
40
106
  };
41
- const applyTextViewSelection = (widget, start, end) => {
42
- const buffer = widget.getBuffer();
43
- buffer.selectRange(buffer.getIterAtOffset(start), buffer.getIterAtOffset(end));
44
- if (end !== start) {
45
- deleteSelection(widget);
46
- }
47
- };
48
- const applyEditableSelection = (widget, start, end) => {
49
- widget.selectRegion(start, end);
50
- if (end !== start) {
51
- widget.deleteSelection();
52
- }
53
- widget.setPosition(start);
54
- };
55
107
  const applyInitialSelection = (widget, options) => {
56
108
  if (options.initialSelectionStart === undefined) {
57
109
  return;
@@ -59,10 +111,11 @@ const applyInitialSelection = (widget, options) => {
59
111
  const start = options.initialSelectionStart;
60
112
  const end = options.initialSelectionEnd ?? start;
61
113
  if (widget instanceof Gtk.TextView) {
62
- applyTextViewSelection(widget, start, end);
114
+ const buffer = widget.getBuffer();
115
+ buffer.selectRange(buffer.getIterAtOffset(start), buffer.getIterAtOffset(end));
63
116
  return;
64
117
  }
65
- applyEditableSelection(widget, start, end);
118
+ widget.selectRegion(start, end);
66
119
  };
67
120
  const writeClipboardText = (widget, text) => {
68
121
  const value = GObject.buildValue(GObject.TYPE_STRING, (v) => {
@@ -80,19 +133,21 @@ const runEditableEvent = (widget, failure, action) => wrapEvent(widget, () => {
80
133
  action(widget);
81
134
  });
82
135
  /**
83
- * Focuses the widget unless `shouldFocus` is false, applies any initial selection, and inserts the text
84
- * at the cursor.
136
+ * Focuses the widget unless `shouldFocus` is false, applies any initial selection, and inserts the
137
+ * text at the cursor, deleting the text the widget has selected first, the way typing over a
138
+ * selection does in GTK4. Focusing works the way clicking into the widget does, leaving its text
139
+ * unselected: a `Gtk.Editable` takes its caret to the end of its text, a `Gtk.TextView` keeps the
140
+ * caret where it stands, and the replaced text stays on the widget's undo stack either way.
85
141
  *
86
142
  * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.
87
143
  */
88
144
  const type = (widget, text, options) => runEditableEvent(widget, "Cannot type into element", (editable) => {
89
145
  if (options?.shouldFocus ?? true) {
90
- editable.grabFocus();
146
+ focusEditable(editable);
91
147
  }
92
148
  applyInitialSelection(editable, options ?? {});
93
- insertEditableText(editable, text);
149
+ insertEditableText(editable, text, "typing");
94
150
  });
95
- const isWidgetEditable = (widget) => callBooleanGetter(widget, "getEditable") ?? true;
96
151
  const hasNonEditableChar = (iter, isDefaultEditable) => {
97
152
  while (!iter.isEnd()) {
98
153
  if (!iter.editable(isDefaultEditable)) {
@@ -136,7 +191,7 @@ const didDeleteAllText = (widget) => {
136
191
  };
137
192
  const clearEditable = (editable) => {
138
193
  requireClearableText(editable);
139
- editable.grabFocus();
194
+ focusEditable(editable);
140
195
  if (readEditableText(editable) === "") {
141
196
  return;
142
197
  }
@@ -173,7 +228,7 @@ const cut = (widget) => runEditableEvent(widget, "Cannot cut", (editable) => {
173
228
  });
174
229
  /**
175
230
  * Inserts the given text at an editable widget's cursor, reading the clipboard instead when no text
176
- * is given.
231
+ * is given, and deleting the text the widget has selected first, the way a paste does in GTK4.
177
232
  *
178
233
  * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.
179
234
  */
@@ -183,7 +238,7 @@ const paste = async (widget, text) => {
183
238
  }
184
239
  const content = text ?? (await widget.getClipboard().readTextAsync(null)) ?? "";
185
240
  await wrapEvent(widget, () => {
186
- insertEditableText(widget, content);
241
+ insertEditableText(widget, content, "pasting");
187
242
  });
188
243
  };
189
244
  export { resetClipboard, type, clear, copy, cut, paste };
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/user-event/text.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gdk from \"@gtkx/gi/gdk\";\nimport * as GObject from \"@gtkx/gi/gobject\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { EDITABLE_ROLES, type EditableTarget, getEditableDelegate, isEditable, readEditableText } from \"../editable.js\";\nimport { formatRoleList } from \"../role-helpers.js\";\nimport { getWidgetSelection } from \"../widget-accessible-properties.js\";\nimport { callBooleanGetter } from \"../widget-getters.js\";\nimport { wrapEvent } from \"./event-wrapper.js\";\n\ntype SignalWatchTarget = {\n on(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;\n off(signal: string, handler: (...args: unknown[]) => unknown): unknown;\n};\n\n/** Options for `userEvent.type`. */\ntype TypeOptions = {\n /** Focus the widget before typing; defaults to true. */\n shouldFocus?: boolean | undefined;\n /** Select from this offset before typing, replacing the selected range with the typed text. */\n initialSelectionStart?: number | undefined;\n /** End offset of the initial selection (defaults to the start offset). */\n initialSelectionEnd?: number | undefined;\n};\n\nconst EDITABLE_REQUIRED = `expected editable widget (${formatRoleList(EDITABLE_ROLES)})`;\nconst CLEAR_REFUSED = \"Cannot clear element: the widget refuses to delete part of its text\";\n\nconst insertEditableText = (widget: EditableTarget, text: string): void => {\n if (widget instanceof Gtk.TextView) {\n widget.emit(\"insert-at-cursor\", text);\n\n return;\n }\n\n const target = getEditableDelegate(widget) ?? widget;\n\n if (target instanceof Gtk.Text) {\n target.emit(\"insert-at-cursor\", text);\n\n return;\n }\n\n const position = widget.getPosition();\n const newPosition = widget.insertText(text, text.length, position);\n widget.setPosition(newPosition);\n};\n\nconst readSelection = (widget: EditableTarget): string => getWidgetSelection(widget) ?? \"\";\n\nconst deleteSelection = (widget: EditableTarget): void => {\n if (widget instanceof Gtk.TextView) {\n widget.getBuffer().deleteSelection(false, true);\n\n return;\n }\n\n widget.deleteSelection();\n};\n\nconst deleteAllText = (widget: EditableTarget): void => {\n if (widget instanceof Gtk.TextView) {\n const buffer = widget.getBuffer();\n buffer.deleteInteractive(buffer.getStartIter(), buffer.getEndIter(), widget.getEditable());\n\n return;\n }\n\n widget.deleteText(0, -1);\n};\n\nconst applyTextViewSelection = (widget: Gtk.TextView, start: number, end: number): void => {\n const buffer = widget.getBuffer();\n buffer.selectRange(buffer.getIterAtOffset(start), buffer.getIterAtOffset(end));\n\n if (end !== start) {\n deleteSelection(widget);\n }\n};\n\nconst applyEditableSelection = (widget: Gtk.Editable, start: number, end: number): void => {\n widget.selectRegion(start, end);\n\n if (end !== start) {\n widget.deleteSelection();\n }\n\n widget.setPosition(start);\n};\n\nconst applyInitialSelection = (widget: EditableTarget, options: TypeOptions): void => {\n if (options.initialSelectionStart === undefined) {\n return;\n }\n\n const start = options.initialSelectionStart;\n const end = options.initialSelectionEnd ?? start;\n\n if (widget instanceof Gtk.TextView) {\n applyTextViewSelection(widget, start, end);\n\n return;\n }\n\n applyEditableSelection(widget, start, end);\n};\n\nconst writeClipboardText = (widget: Gtk.Widget, text: string): void => {\n const value = GObject.buildValue(GObject.TYPE_STRING, (v) => {\n v.setString(text);\n });\n\n widget.getClipboard().set(value);\n};\n\nconst resetClipboard = (): void => {\n Gdk.Display.getDefault()?.getClipboard().setContent(null);\n};\n\nconst runEditableEvent = (\n widget: Gtk.Widget,\n failure: string,\n action: (editable: EditableTarget) => void,\n): Promise<void> =>\n wrapEvent(widget, () => {\n if (!isEditable(widget)) {\n throw new Error(`${failure}: ${EDITABLE_REQUIRED}`);\n }\n\n action(widget);\n });\n\n/**\n * Focuses the widget unless `shouldFocus` is false, applies any initial selection, and inserts the text\n * at the cursor.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst type = (widget: Gtk.Widget, text: string, options?: TypeOptions): Promise<void> =>\n runEditableEvent(widget, \"Cannot type into element\", (editable) => {\n if (options?.shouldFocus ?? true) {\n editable.grabFocus();\n }\n\n applyInitialSelection(editable, options ?? {});\n insertEditableText(editable, text);\n });\n\nconst isWidgetEditable = (widget: Gtk.Widget): boolean => callBooleanGetter(widget, \"getEditable\") ?? true;\n\nconst hasNonEditableChar = (iter: Gtk.TextIter, isDefaultEditable: boolean): boolean => {\n while (!iter.isEnd()) {\n if (!iter.editable(isDefaultEditable)) {\n return true;\n }\n\n if (!iter.forwardChar()) {\n return false;\n }\n }\n\n return false;\n};\n\nconst hasProtectedText = (widget: EditableTarget): boolean =>\n widget instanceof Gtk.TextView && hasNonEditableChar(widget.getBuffer().getStartIter(), widget.getEditable());\n\nconst requireClearableText = (editable: EditableTarget): void => {\n if (!isWidgetEditable(editable)) {\n throw new Error(\"Cannot clear element: the widget is not editable\");\n }\n\n if (hasProtectedText(editable)) {\n throw new Error(CLEAR_REFUSED);\n }\n};\n\nconst deletionSignalFor = (widget: EditableTarget): [SignalWatchTarget, string] =>\n widget instanceof Gtk.TextView ? [widget.getBuffer(), \"delete-range\"] : [widget, \"delete-text\"];\n\nconst isDeletionApplied = (emitter: SignalWatchTarget, signal: string, apply: () => void): boolean => {\n let isApplied = false;\n\n const watch = (): void => {\n isApplied = true;\n };\n\n emitter.on(signal, watch, true);\n\n try {\n apply();\n } finally {\n emitter.off(signal, watch);\n }\n\n return isApplied;\n};\n\nconst didDeleteAllText = (widget: EditableTarget): boolean => {\n const [emitter, signal] = deletionSignalFor(widget);\n\n return isDeletionApplied(emitter, signal, () => {\n deleteAllText(widget);\n });\n};\n\nconst clearEditable = (editable: EditableTarget): void => {\n requireClearableText(editable);\n editable.grabFocus();\n\n if (readEditableText(editable) === \"\") {\n return;\n }\n\n if (!didDeleteAllText(editable)) {\n throw new Error(CLEAR_REFUSED);\n }\n};\n\n/**\n * Focuses an editable widget and deletes its whole text through the widget's own editing API, so the\n * deletion emits the signals an edit made by hand emits. Text a tag protects is detected before the\n * deletion runs, leaving such a widget untouched. A widget that writes a new value back in response\n * to the deletion, as an input mask does, is cleared successfully and keeps whatever it wrote.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView, when it refuses edits, when\n * a tag protects part of its text, or when a handler blocks the deletion.\n */\nconst clear = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot clear element\", clearEditable);\n\n/**\n * Writes an editable widget's selected text to its clipboard, or the empty string when nothing is selected.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst copy = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot copy\", (editable) => {\n writeClipboardText(editable, readSelection(editable));\n });\n\n/**\n * Writes an editable widget's selected text to its clipboard, then deletes that selection.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst cut = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot cut\", (editable) => {\n writeClipboardText(editable, readSelection(editable));\n deleteSelection(editable);\n });\n\n/**\n * Inserts the given text at an editable widget's cursor, reading the clipboard instead when no text\n * is given.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst paste = async (widget: Gtk.Widget, text?: string): Promise<void> => {\n if (!isEditable(widget)) {\n throw new Error(`Cannot paste: ${EDITABLE_REQUIRED}`);\n }\n\n const content = text ?? (await widget.getClipboard().readTextAsync(null)) ?? \"\";\n\n await wrapEvent(widget, () => {\n insertEditableText(widget, content);\n });\n};\n\nexport { resetClipboard, type, clear, copy, cut, paste, type TypeOptions };\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/user-event/text.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gdk from \"@gtkx/gi/gdk\";\nimport * as GObject from \"@gtkx/gi/gobject\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { EDITABLE_ROLES, type EditableTarget, getEditableDelegate, isEditable, readEditableText } from \"../editable.js\";\nimport { formatRoleList } from \"../role-helpers.js\";\nimport { getWidgetSelection } from \"../widget-accessible-properties.js\";\nimport { callBooleanGetter } from \"../widget-getters.js\";\nimport { wrapEvent } from \"./event-wrapper.js\";\n\ntype SignalWatchTarget = {\n on(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): unknown;\n off(signal: string, handler: (...args: unknown[]) => unknown): unknown;\n};\n\ntype InsertKind = \"typing\" | \"pasting\";\n\n/** Options for `userEvent.type`. */\ntype TypeOptions = {\n /** Focus the widget before typing; defaults to true. */\n shouldFocus?: boolean | undefined;\n /** Select from this offset before typing, replacing the selected range with the typed text. */\n initialSelectionStart?: number | undefined;\n /** End offset of the initial selection (defaults to the start offset). */\n initialSelectionEnd?: number | undefined;\n};\n\nconst EDITABLE_REQUIRED = `expected editable widget (${formatRoleList(EDITABLE_ROLES)})`;\nconst CLEAR_REFUSED = \"Cannot clear element: the widget refuses to delete part of its text\";\nconst SELECTION_DELETE_COUNT = 1;\nconst SINGLE_CHARACTER_LENGTH = 1;\nconst TEXT_END_POSITION = -1;\n\nconst isWidgetEditable = (widget: Gtk.Widget): boolean => callBooleanGetter(widget, \"getEditable\") ?? true;\nconst readSelection = (widget: EditableTarget): string => getWidgetSelection(widget) ?? \"\";\nconst hasSelectedText = (widget: Gtk.Editable): boolean => widget.getSelectionBounds()[0];\n\nconst getDelegateText = (widget: Gtk.Editable): Gtk.Text | null => {\n const delegate = getEditableDelegate(widget) ?? widget;\n\n return delegate instanceof Gtk.Text ? delegate : null;\n};\n\nconst getSelectionLength = (buffer: Gtk.TextBuffer): number => {\n const [isSelected, start, end] = buffer.getSelectionBounds();\n\n return isSelected ? end.getOffset() - start.getOffset() : 0;\n};\n\nconst deleteSelection = (widget: EditableTarget): void => {\n if (widget instanceof Gtk.TextView) {\n widget.getBuffer().deleteSelection(false, true);\n\n return;\n }\n\n widget.deleteSelection();\n};\n\nconst focusPlainEditable = (widget: Gtk.Editable): void => {\n const [isSelected, start, end] = widget.getSelectionBounds();\n widget.grabFocus();\n\n if (isSelected) {\n widget.selectRegion(start, end);\n\n return;\n }\n\n widget.setPosition(TEXT_END_POSITION);\n};\n\nconst focusEditable = (widget: EditableTarget): void => {\n if (widget instanceof Gtk.TextView) {\n widget.grabFocus();\n\n return;\n }\n\n const target = getDelegateText(widget);\n\n if (target === null) {\n focusPlainEditable(widget);\n\n return;\n }\n\n target.grabFocusWithoutSelecting();\n\n if (!hasSelectedText(widget)) {\n widget.setPosition(TEXT_END_POSITION);\n }\n};\n\nconst insertTextViewText = (widget: Gtk.TextView, text: string, kind: InsertKind): void => {\n const buffer = widget.getBuffer();\n const length = getSelectionLength(buffer);\n buffer.beginUserAction();\n\n if (length > 0) {\n widget.emit(\"delete-from-cursor\", Gtk.DeleteType.CHARS, SELECTION_DELETE_COUNT);\n\n if (kind === \"typing\" && length > SINGLE_CHARACTER_LENGTH) {\n buffer.endUserAction();\n buffer.beginUserAction();\n }\n }\n\n widget.emit(\"insert-at-cursor\", text);\n buffer.endUserAction();\n};\n\nconst insertDelegateText = (target: Gtk.Text, text: string): void => {\n if (hasSelectedText(target)) {\n target.emit(\"delete-from-cursor\", Gtk.DeleteType.CHARS, SELECTION_DELETE_COUNT);\n }\n\n target.emit(\"insert-at-cursor\", text);\n};\n\nconst insertPlainText = (widget: Gtk.Editable, text: string): void => {\n if (!isWidgetEditable(widget)) {\n return;\n }\n\n if (hasSelectedText(widget)) {\n widget.deleteSelection();\n }\n\n const position = widget.getPosition();\n const newPosition = widget.insertText(text, text.length, position);\n widget.setPosition(newPosition);\n};\n\nconst insertEditableText = (widget: EditableTarget, text: string, kind: InsertKind): void => {\n if (widget instanceof Gtk.TextView) {\n insertTextViewText(widget, text, kind);\n\n return;\n }\n\n const target = getDelegateText(widget);\n\n if (target === null) {\n insertPlainText(widget, text);\n\n return;\n }\n\n insertDelegateText(target, text);\n};\n\nconst deleteAllText = (widget: EditableTarget): void => {\n if (widget instanceof Gtk.TextView) {\n const buffer = widget.getBuffer();\n buffer.deleteInteractive(buffer.getStartIter(), buffer.getEndIter(), widget.getEditable());\n\n return;\n }\n\n widget.deleteText(0, -1);\n};\n\nconst applyInitialSelection = (widget: EditableTarget, options: TypeOptions): void => {\n if (options.initialSelectionStart === undefined) {\n return;\n }\n\n const start = options.initialSelectionStart;\n const end = options.initialSelectionEnd ?? start;\n\n if (widget instanceof Gtk.TextView) {\n const buffer = widget.getBuffer();\n buffer.selectRange(buffer.getIterAtOffset(start), buffer.getIterAtOffset(end));\n\n return;\n }\n\n widget.selectRegion(start, end);\n};\n\nconst writeClipboardText = (widget: Gtk.Widget, text: string): void => {\n const value = GObject.buildValue(GObject.TYPE_STRING, (v) => {\n v.setString(text);\n });\n\n widget.getClipboard().set(value);\n};\n\nconst resetClipboard = (): void => {\n Gdk.Display.getDefault()?.getClipboard().setContent(null);\n};\n\nconst runEditableEvent = (\n widget: Gtk.Widget,\n failure: string,\n action: (editable: EditableTarget) => void,\n): Promise<void> =>\n wrapEvent(widget, () => {\n if (!isEditable(widget)) {\n throw new Error(`${failure}: ${EDITABLE_REQUIRED}`);\n }\n\n action(widget);\n });\n\n/**\n * Focuses the widget unless `shouldFocus` is false, applies any initial selection, and inserts the\n * text at the cursor, deleting the text the widget has selected first, the way typing over a\n * selection does in GTK4. Focusing works the way clicking into the widget does, leaving its text\n * unselected: a `Gtk.Editable` takes its caret to the end of its text, a `Gtk.TextView` keeps the\n * caret where it stands, and the replaced text stays on the widget's undo stack either way.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst type = (widget: Gtk.Widget, text: string, options?: TypeOptions): Promise<void> =>\n runEditableEvent(widget, \"Cannot type into element\", (editable) => {\n if (options?.shouldFocus ?? true) {\n focusEditable(editable);\n }\n\n applyInitialSelection(editable, options ?? {});\n insertEditableText(editable, text, \"typing\");\n });\n\nconst hasNonEditableChar = (iter: Gtk.TextIter, isDefaultEditable: boolean): boolean => {\n while (!iter.isEnd()) {\n if (!iter.editable(isDefaultEditable)) {\n return true;\n }\n\n if (!iter.forwardChar()) {\n return false;\n }\n }\n\n return false;\n};\n\nconst hasProtectedText = (widget: EditableTarget): boolean =>\n widget instanceof Gtk.TextView && hasNonEditableChar(widget.getBuffer().getStartIter(), widget.getEditable());\n\nconst requireClearableText = (editable: EditableTarget): void => {\n if (!isWidgetEditable(editable)) {\n throw new Error(\"Cannot clear element: the widget is not editable\");\n }\n\n if (hasProtectedText(editable)) {\n throw new Error(CLEAR_REFUSED);\n }\n};\n\nconst deletionSignalFor = (widget: EditableTarget): [SignalWatchTarget, string] =>\n widget instanceof Gtk.TextView ? [widget.getBuffer(), \"delete-range\"] : [widget, \"delete-text\"];\n\nconst isDeletionApplied = (emitter: SignalWatchTarget, signal: string, apply: () => void): boolean => {\n let isApplied = false;\n\n const watch = (): void => {\n isApplied = true;\n };\n\n emitter.on(signal, watch, true);\n\n try {\n apply();\n } finally {\n emitter.off(signal, watch);\n }\n\n return isApplied;\n};\n\nconst didDeleteAllText = (widget: EditableTarget): boolean => {\n const [emitter, signal] = deletionSignalFor(widget);\n\n return isDeletionApplied(emitter, signal, () => {\n deleteAllText(widget);\n });\n};\n\nconst clearEditable = (editable: EditableTarget): void => {\n requireClearableText(editable);\n focusEditable(editable);\n\n if (readEditableText(editable) === \"\") {\n return;\n }\n\n if (!didDeleteAllText(editable)) {\n throw new Error(CLEAR_REFUSED);\n }\n};\n\n/**\n * Focuses an editable widget and deletes its whole text through the widget's own editing API, so the\n * deletion emits the signals an edit made by hand emits. Text a tag protects is detected before the\n * deletion runs, leaving such a widget untouched. A widget that writes a new value back in response\n * to the deletion, as an input mask does, is cleared successfully and keeps whatever it wrote.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView, when it refuses edits, when\n * a tag protects part of its text, or when a handler blocks the deletion.\n */\nconst clear = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot clear element\", clearEditable);\n\n/**\n * Writes an editable widget's selected text to its clipboard, or the empty string when nothing is selected.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst copy = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot copy\", (editable) => {\n writeClipboardText(editable, readSelection(editable));\n });\n\n/**\n * Writes an editable widget's selected text to its clipboard, then deletes that selection.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst cut = (widget: Gtk.Widget): Promise<void> =>\n runEditableEvent(widget, \"Cannot cut\", (editable) => {\n writeClipboardText(editable, readSelection(editable));\n deleteSelection(editable);\n });\n\n/**\n * Inserts the given text at an editable widget's cursor, reading the clipboard instead when no text\n * is given, and deleting the text the widget has selected first, the way a paste does in GTK4.\n *\n * @throws When the widget is neither a Gtk.Editable nor a Gtk.TextView.\n */\nconst paste = async (widget: Gtk.Widget, text?: string): Promise<void> => {\n if (!isEditable(widget)) {\n throw new Error(`Cannot paste: ${EDITABLE_REQUIRED}`);\n }\n\n const content = text ?? (await widget.getClipboard().readTextAsync(null)) ?? \"\";\n\n await wrapEvent(widget, () => {\n insertEditableText(widget, content, \"pasting\");\n });\n};\n\nexport { resetClipboard, type, clear, copy, cut, paste, type TypeOptions };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"wait-for.d.ts","sourceRoot":"","sources":["../src/wait-for.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,mEAAmE;AACnE,KAAK,aAAa,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AACtD,4FAA4F;AAC5F,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,aAAa,CAAC,CAAC;AA6D3E;;;;;;;GAOG;AACH,QAAA,MAAM,OAAO,GAAI,CAAC,EAAE,UAAU,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,cAAc,KAAG,OAAO,CAAC,CAAC,CAoBvF,CAAC;AAyBF;;;;;;;GAOG;AACH,QAAA,MAAM,yBAAyB,GAC3B,mBAAmB,iBAAiB,EACpC,UAAU,cAAc,KACzB,OAAO,CAAC,IAAI,CAmBd,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC"}
1
+ {"version":3,"file":"wait-for.d.ts","sourceRoot":"","sources":["../src/wait-for.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOjD,mEAAmE;AACnE,KAAK,aAAa,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;AACtD,4FAA4F;AAC5F,KAAK,iBAAiB,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,aAAa,CAAC,CAAC;AA4D3E;;;;;;;GAOG;AACH,QAAA,MAAM,OAAO,GAAI,CAAC,EAAE,UAAU,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,cAAc,KAAG,OAAO,CAAC,CAAC,CAoBvF,CAAC;AAyBF;;;;;;;GAOG;AACH,QAAA,MAAM,yBAAyB,GAC3B,mBAAmB,iBAAiB,EACpC,UAAU,cAAc,KACzB,OAAO,CAAC,IAAI,CAmBd,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC"}
package/dist/wait-for.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { runWithActEnvironment } from "./act.js";
2
2
  import { getConfig } from "./config.js";
3
3
  import { timeoutError } from "./errors.js";
4
+ import { advanceFakeClock, delay, now } from "./timers.js";
4
5
  const DEFAULT_INTERVAL = 50;
5
6
  const ELEMENT_NOT_REMOVED = new Error("Element not yet removed");
6
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
7
  const copyStackTrace = (target, source) => {
8
8
  const { stack } = source;
9
9
  if (stack === undefined) {
@@ -17,9 +17,9 @@ const copyStackTrace = (target, source) => {
17
17
  target.stack = stack.slice(0, index) + target.message + stack.slice(index + source.message.length);
18
18
  };
19
19
  const pollUntilSuccess = async (callback, timeout, interval) => {
20
- const startTime = Date.now();
20
+ const startTime = now();
21
21
  let lastError = null;
22
- while (Date.now() - startTime < timeout) {
22
+ while (now() - startTime < timeout) {
23
23
  try {
24
24
  const result = await callback();
25
25
  await delay(0);
@@ -27,6 +27,7 @@ const pollUntilSuccess = async (callback, timeout, interval) => {
27
27
  }
28
28
  catch (error) {
29
29
  lastError = error;
30
+ await advanceFakeClock(interval);
30
31
  await delay(interval);
31
32
  }
32
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wait-for.js","sourceRoot":"","sources":["../src/wait-for.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport type { WaitForOptions } from \"./types.js\";\nimport { runWithActEnvironment } from \"./act.js\";\nimport { getConfig } from \"./config.js\";\nimport { timeoutError } from \"./errors.js\";\n\ntype PollResult<T> = { status: \"resolved\"; value: T } | { status: \"timedout\"; lastError: Error | null };\n/** Widgets watched for removal; null counts as already removed. */\ntype RemovalTarget = Gtk.Widget | Gtk.Widget[] | null;\n/** The widgets to watch for removal, or a function re-read on every poll to locate them. */\ntype ElementOrCallback = Gtk.Widget | Gtk.Widget[] | (() => RemovalTarget);\n\nconst DEFAULT_INTERVAL = 50;\nconst ELEMENT_NOT_REMOVED = new Error(\"Element not yet removed\");\n\nconst delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst copyStackTrace = (target: Error, source: Error): void => {\n const { stack } = source;\n\n if (stack === undefined) {\n return;\n }\n\n const index = stack.indexOf(source.message);\n\n if (index === -1) {\n target.stack = stack;\n\n return;\n }\n\n target.stack = stack.slice(0, index) + target.message + stack.slice(index + source.message.length);\n};\n\nconst pollUntilSuccess = async <T>(\n callback: () => T | Promise<T>,\n timeout: number,\n interval: number,\n): Promise<PollResult<T>> => {\n const startTime = Date.now();\n let lastError: Error | null = null;\n\n while (Date.now() - startTime < timeout) {\n try {\n const result = await callback();\n await delay(0);\n\n return { status: \"resolved\", value: result };\n } catch (error) {\n lastError = error as Error;\n await delay(interval);\n }\n }\n\n return { status: \"timedout\", lastError };\n};\n\nconst buildTimeoutError = (\n timeout: number,\n lastError: Error | null,\n stackTraceError: Error,\n onTimeout: ((error: Error) => Error) | undefined,\n): Error => {\n const error = timeoutError(timeout, lastError);\n const finalError = onTimeout ? onTimeout(error) : error;\n copyStackTrace(finalError, stackTraceError);\n\n return finalError;\n};\n\n/**\n * Repeatedly invokes a callback until it succeeds without throwing or the\n * timeout elapses, retrying on each rejection at a fixed interval.\n *\n * @param callback The assertion or query to retry; its resolved value is returned.\n * @param options Optional timeout, interval, and timeout error customization.\n * @returns The callback's result once it succeeds.\n */\nconst waitFor = <T>(callback: () => T | Promise<T>, options?: WaitForOptions): Promise<T> => {\n if (typeof callback !== \"function\") {\n throw new TypeError(\"Received `callback` arg must be a function\");\n }\n\n const stackTraceError = options?.stackTraceError ?? new Error(\"STACK_TRACE_MESSAGE\");\n\n return Promise.resolve(\n runWithActEnvironment(false, async () => {\n const config = getConfig();\n const { timeout = config.asyncUtilTimeout, interval = DEFAULT_INTERVAL, onTimeout } = options ?? {};\n const result = await pollUntilSuccess(callback, timeout, interval);\n\n if (result.status === \"resolved\") {\n return result.value;\n }\n\n throw buildTimeoutError(timeout, result.lastError, stackTraceError, onTimeout);\n }),\n );\n};\n\nconst getTarget = (elementOrCallback: ElementOrCallback): RemovalTarget =>\n typeof elementOrCallback === \"function\" ? elementOrCallback() : elementOrCallback;\n\nconst isWidgetRemoved = (widget: Gtk.Widget | null): boolean => {\n if (widget === null) {\n return true;\n }\n\n try {\n return widget.getRoot() === null;\n } catch {\n return true;\n }\n};\n\nconst isTargetRemoved = (target: RemovalTarget): boolean => {\n if (Array.isArray(target)) {\n return target.every((widget) => isWidgetRemoved(widget));\n }\n\n return isWidgetRemoved(target);\n};\n\n/**\n * Waits until the given widget or widgets are detached from the tree. Rejects\n * immediately if the target is already absent when called.\n *\n * @param elementOrCallback The widget, array of widgets, or a function\n * returning them to observe for removal.\n * @param options Optional timeout and interval settings.\n */\nconst waitForElementToBeRemoved = (\n elementOrCallback: ElementOrCallback,\n options?: WaitForOptions,\n): Promise<void> => {\n const stackTraceError = new Error(\"STACK_TRACE_MESSAGE\");\n\n if (isTargetRemoved(getTarget(elementOrCallback))) {\n return Promise.reject(\n new Error(\n \"Element already removed: waitForElementToBeRemoved requires the element to be present initially\",\n ),\n );\n }\n\n return waitFor(\n () => {\n if (!isTargetRemoved(getTarget(elementOrCallback))) {\n throw ELEMENT_NOT_REMOVED;\n }\n },\n { ...options, stackTraceError },\n );\n};\n\nexport { waitFor, waitForElementToBeRemoved };\n"]}
1
+ {"version":3,"file":"wait-for.js","sourceRoot":"","sources":["../src/wait-for.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAQ3D,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,mBAAmB,GAAG,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAEjE,MAAM,cAAc,GAAG,CAAC,MAAa,EAAE,MAAa,EAAQ,EAAE;IAC1D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO;IACX,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE5C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QAErB,OAAO;IACX,CAAC;IAED,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACvG,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,KAAK,EAC1B,QAA8B,EAC9B,OAAe,EACf,QAAgB,EACM,EAAE;IACxB,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC;IACxB,IAAI,SAAS,GAAiB,IAAI,CAAC;IAEnC,OAAO,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAC;YAChC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;YAEf,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,SAAS,GAAG,KAAc,CAAC;YAC3B,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YACjC,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACtB,OAAe,EACf,SAAuB,EACvB,eAAsB,EACtB,SAAgD,EAC3C,EAAE;IACP,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACxD,cAAc,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAE5C,OAAO,UAAU,CAAC;AACtB,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG,CAAI,QAA8B,EAAE,OAAwB,EAAc,EAAE;IACxF,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,IAAI,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAErF,OAAO,OAAO,CAAC,OAAO,CAClB,qBAAqB,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,gBAAgB,EAAE,QAAQ,GAAG,gBAAgB,EAAE,SAAS,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QACpG,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEnE,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAO,MAAM,CAAC,KAAK,CAAC;QACxB,CAAC;QAED,MAAM,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;IACnF,CAAC,CAAC,CACL,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,iBAAoC,EAAiB,EAAE,CACtE,OAAO,iBAAiB,KAAK,UAAU,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;AAEtF,MAAM,eAAe,GAAG,CAAC,MAAyB,EAAW,EAAE;IAC3D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,IAAI,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,MAAqB,EAAW,EAAE;IACvD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,yBAAyB,GAAG,CAC9B,iBAAoC,EACpC,OAAwB,EACX,EAAE;IACf,MAAM,eAAe,GAAG,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAEzD,IAAI,eAAe,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC;QAChD,OAAO,OAAO,CAAC,MAAM,CACjB,IAAI,KAAK,CACL,iGAAiG,CACpG,CACJ,CAAC;IACN,CAAC;IAED,OAAO,OAAO,CACV,GAAG,EAAE;QACD,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,mBAAmB,CAAC;QAC9B,CAAC;IACL,CAAC,EACD,EAAE,GAAG,OAAO,EAAE,eAAe,EAAE,CAClC,CAAC;AACN,CAAC,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport type { WaitForOptions } from \"./types.js\";\nimport { runWithActEnvironment } from \"./act.js\";\nimport { getConfig } from \"./config.js\";\nimport { timeoutError } from \"./errors.js\";\nimport { advanceFakeClock, delay, now } from \"./timers.js\";\n\ntype PollResult<T> = { status: \"resolved\"; value: T } | { status: \"timedout\"; lastError: Error | null };\n/** Widgets watched for removal; null counts as already removed. */\ntype RemovalTarget = Gtk.Widget | Gtk.Widget[] | null;\n/** The widgets to watch for removal, or a function re-read on every poll to locate them. */\ntype ElementOrCallback = Gtk.Widget | Gtk.Widget[] | (() => RemovalTarget);\n\nconst DEFAULT_INTERVAL = 50;\nconst ELEMENT_NOT_REMOVED = new Error(\"Element not yet removed\");\n\nconst copyStackTrace = (target: Error, source: Error): void => {\n const { stack } = source;\n\n if (stack === undefined) {\n return;\n }\n\n const index = stack.indexOf(source.message);\n\n if (index === -1) {\n target.stack = stack;\n\n return;\n }\n\n target.stack = stack.slice(0, index) + target.message + stack.slice(index + source.message.length);\n};\n\nconst pollUntilSuccess = async <T>(\n callback: () => T | Promise<T>,\n timeout: number,\n interval: number,\n): Promise<PollResult<T>> => {\n const startTime = now();\n let lastError: Error | null = null;\n\n while (now() - startTime < timeout) {\n try {\n const result = await callback();\n await delay(0);\n\n return { status: \"resolved\", value: result };\n } catch (error) {\n lastError = error as Error;\n await advanceFakeClock(interval);\n await delay(interval);\n }\n }\n\n return { status: \"timedout\", lastError };\n};\n\nconst buildTimeoutError = (\n timeout: number,\n lastError: Error | null,\n stackTraceError: Error,\n onTimeout: ((error: Error) => Error) | undefined,\n): Error => {\n const error = timeoutError(timeout, lastError);\n const finalError = onTimeout ? onTimeout(error) : error;\n copyStackTrace(finalError, stackTraceError);\n\n return finalError;\n};\n\n/**\n * Repeatedly invokes a callback until it succeeds without throwing or the\n * timeout elapses, retrying on each rejection at a fixed interval.\n *\n * @param callback The assertion or query to retry; its resolved value is returned.\n * @param options Optional timeout, interval, and timeout error customization.\n * @returns The callback's result once it succeeds.\n */\nconst waitFor = <T>(callback: () => T | Promise<T>, options?: WaitForOptions): Promise<T> => {\n if (typeof callback !== \"function\") {\n throw new TypeError(\"Received `callback` arg must be a function\");\n }\n\n const stackTraceError = options?.stackTraceError ?? new Error(\"STACK_TRACE_MESSAGE\");\n\n return Promise.resolve(\n runWithActEnvironment(false, async () => {\n const config = getConfig();\n const { timeout = config.asyncUtilTimeout, interval = DEFAULT_INTERVAL, onTimeout } = options ?? {};\n const result = await pollUntilSuccess(callback, timeout, interval);\n\n if (result.status === \"resolved\") {\n return result.value;\n }\n\n throw buildTimeoutError(timeout, result.lastError, stackTraceError, onTimeout);\n }),\n );\n};\n\nconst getTarget = (elementOrCallback: ElementOrCallback): RemovalTarget =>\n typeof elementOrCallback === \"function\" ? elementOrCallback() : elementOrCallback;\n\nconst isWidgetRemoved = (widget: Gtk.Widget | null): boolean => {\n if (widget === null) {\n return true;\n }\n\n try {\n return widget.getRoot() === null;\n } catch {\n return true;\n }\n};\n\nconst isTargetRemoved = (target: RemovalTarget): boolean => {\n if (Array.isArray(target)) {\n return target.every((widget) => isWidgetRemoved(widget));\n }\n\n return isWidgetRemoved(target);\n};\n\n/**\n * Waits until the given widget or widgets are detached from the tree. Rejects\n * immediately if the target is already absent when called.\n *\n * @param elementOrCallback The widget, array of widgets, or a function\n * returning them to observe for removal.\n * @param options Optional timeout and interval settings.\n */\nconst waitForElementToBeRemoved = (\n elementOrCallback: ElementOrCallback,\n options?: WaitForOptions,\n): Promise<void> => {\n const stackTraceError = new Error(\"STACK_TRACE_MESSAGE\");\n\n if (isTargetRemoved(getTarget(elementOrCallback))) {\n return Promise.reject(\n new Error(\n \"Element already removed: waitForElementToBeRemoved requires the element to be present initially\",\n ),\n );\n }\n\n return waitFor(\n () => {\n if (!isTargetRemoved(getTarget(elementOrCallback))) {\n throw ELEMENT_NOT_REMOVED;\n }\n },\n { ...options, stackTraceError },\n );\n};\n\nexport { waitFor, waitForElementToBeRemoved };\n"]}
@@ -1,8 +1,11 @@
1
+ import type * as GObject from "@gtkx/gi/gobject";
1
2
  import type * as Gtk from "@gtkx/gi/gtk";
2
3
  declare const getWidgetTypeName: (widget: Gtk.Widget) => string | null;
4
+ declare const getTypeTag: (object: GObject.Object) => string;
5
+ declare const isDefaultWidgetName: (widget: Gtk.Widget, name: string) => boolean;
3
6
  declare const hasWidgetMethod: (widget: Gtk.Widget, name: string) => boolean;
4
7
  declare const getCallableMethod: <Args extends unknown[], Result>(widget: Gtk.Widget, name: string) => ((...args: Args) => Result) | null;
5
8
  declare const callBooleanGetter: (widget: Gtk.Widget, method: string) => boolean | null;
6
9
  declare const callStringGetter: (widget: Gtk.Widget, method: string) => string | null;
7
- export { callBooleanGetter, callStringGetter, getCallableMethod, getWidgetTypeName, hasWidgetMethod };
10
+ export { callBooleanGetter, callStringGetter, getCallableMethod, getTypeTag, getWidgetTypeName, hasWidgetMethod, isDefaultWidgetName, };
8
11
  //# sourceMappingURL=widget-getters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"widget-getters.d.ts","sourceRoot":"","sources":["../src/widget-getters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAIzC,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAAM,GAAG,IAAyC,CAAC;AAEnG,QAAA,MAAM,eAAe,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,MAAM,KAAG,OACL,CAAC;AAGxD,QAAA,MAAM,iBAAiB,GAAI,IAAI,SAAS,OAAO,EAAE,EAAE,MAAM,EACrD,QAAQ,GAAG,CAAC,MAAM,EAClB,MAAM,MAAM,KACb,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,GAAG,IAIhC,CAAC;AAEF,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,QAAQ,MAAM,KAAG,OAAO,GAAG,IAIzE,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,QAAQ,MAAM,KAAG,MAAM,GAAG,IAIvE,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"widget-getters.d.ts","sourceRoot":"","sources":["../src/widget-getters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,OAAO,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAQzC,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAAM,GAAG,IAAyC,CAAC;AA2BnG,QAAA,MAAM,UAAU,GAAI,QAAQ,OAAO,CAAC,MAAM,KAAG,MAU5C,CAAC;AAEF,QAAA,MAAM,mBAAmB,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,MAAM,KAAG,OACL,CAAC;AAE5D,QAAA,MAAM,eAAe,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,MAAM,KAAG,OACL,CAAC;AAGxD,QAAA,MAAM,iBAAiB,GAAI,IAAI,SAAS,OAAO,EAAE,EAAE,MAAM,EACrD,QAAQ,GAAG,CAAC,MAAM,EAClB,MAAM,MAAM,KACb,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,GAAG,IAIhC,CAAC;AAEF,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,QAAQ,MAAM,KAAG,OAAO,GAAG,IAIzE,CAAC;AAEF,QAAA,MAAM,gBAAgB,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,QAAQ,MAAM,KAAG,MAAM,GAAG,IAIvE,CAAC;AAEF,OAAO,EACH,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,mBAAmB,GACtB,CAAC"}
@@ -1,6 +1,33 @@
1
- import { getInstanceType, typeName } from "@gtkx/runtime";
1
+ import { getClassType, getInstanceType, TYPE_INVALID, typeName } from "@gtkx/runtime";
2
+ import { resolveWrapperClass } from "@gtkx/runtime/internal";
3
+ import { walkClassChain } from "@gtkx/utils";
4
+ const UNNAMED_TYPE_TAG = "Object";
2
5
  const getWidgetMethod = (widget, name) => Reflect.get(widget, name);
3
6
  const getWidgetTypeName = (widget) => typeName(getInstanceType(widget));
7
+ const isClass = (value) => typeof value === "function";
8
+ const nonEmpty = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
9
+ const getObjectClass = (object) => {
10
+ const cls = object.constructor;
11
+ return isClass(cls) ? cls : null;
12
+ };
13
+ const getNearestClassName = (object) => walkClassChain(getObjectClass(object), (ancestor) => nonEmpty(ancestor.name));
14
+ const getUntypedClassName = (object) => {
15
+ const cls = getObjectClass(object);
16
+ return cls !== null && getClassType(cls) === TYPE_INVALID ? nonEmpty(cls.name) : undefined;
17
+ };
18
+ const getExactWrapperName = (type) => {
19
+ const wrapper = resolveWrapperClass(type);
20
+ return wrapper !== null && getClassType(wrapper) === type ? nonEmpty(wrapper.name) : undefined;
21
+ };
22
+ const getTypeTag = (object) => {
23
+ const type = getInstanceType(object);
24
+ return (getUntypedClassName(object) ??
25
+ getExactWrapperName(type) ??
26
+ nonEmpty(typeName(type)) ??
27
+ getNearestClassName(object) ??
28
+ UNNAMED_TYPE_TAG);
29
+ };
30
+ const isDefaultWidgetName = (widget, name) => name.length === 0 || name === getWidgetTypeName(widget);
4
31
  const hasWidgetMethod = (widget, name) => typeof getWidgetMethod(widget, name) === "function";
5
32
  /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */
6
33
  const getCallableMethod = (widget, name) => {
@@ -15,5 +42,5 @@ const callStringGetter = (widget, method) => {
15
42
  const value = getCallableMethod(widget, method)?.();
16
43
  return typeof value === "string" ? value : null;
17
44
  };
18
- export { callBooleanGetter, callStringGetter, getCallableMethod, getWidgetTypeName, hasWidgetMethod };
45
+ export { callBooleanGetter, callStringGetter, getCallableMethod, getTypeTag, getWidgetTypeName, hasWidgetMethod, isDefaultWidgetName, };
19
46
  //# sourceMappingURL=widget-getters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"widget-getters.js","sourceRoot":"","sources":["../src/widget-getters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE1D,MAAM,eAAe,GAAG,CAAC,MAAkB,EAAE,IAAY,EAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACjG,MAAM,iBAAiB,GAAG,CAAC,MAAkB,EAAiB,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnG,MAAM,eAAe,GAAG,CAAC,MAAkB,EAAE,IAAY,EAAW,EAAE,CAClE,OAAO,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,UAAU,CAAC;AAExD,8GAA8G;AAC9G,MAAM,iBAAiB,GAAG,CACtB,MAAkB,EAClB,IAAY,EACsB,EAAE;IACpC,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEzC,OAAO,OAAO,EAAE,KAAK,UAAU,CAAC,CAAC,CAAE,EAAgC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5F,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,MAAkB,EAAE,MAAc,EAAkB,EAAE;IAC7E,MAAM,KAAK,GAAG,iBAAiB,CAAc,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;IAEjE,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,MAAkB,EAAE,MAAc,EAAiB,EAAE;IAC3E,MAAM,KAAK,GAAG,iBAAiB,CAAc,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;IAEjE,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport { getInstanceType, typeName } from \"@gtkx/runtime\";\n\nconst getWidgetMethod = (widget: Gtk.Widget, name: string): unknown => Reflect.get(widget, name);\nconst getWidgetTypeName = (widget: Gtk.Widget): string | null => typeName(getInstanceType(widget));\n\nconst hasWidgetMethod = (widget: Gtk.Widget, name: string): boolean =>\n typeof getWidgetMethod(widget, name) === \"function\";\n\n/* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */\nconst getCallableMethod = <Args extends unknown[], Result>(\n widget: Gtk.Widget,\n name: string,\n): ((...args: Args) => Result) | null => {\n const fn = getWidgetMethod(widget, name);\n\n return typeof fn === \"function\" ? (fn as (...args: Args) => Result).bind(widget) : null;\n};\n\nconst callBooleanGetter = (widget: Gtk.Widget, method: string): boolean | null => {\n const value = getCallableMethod<[], unknown>(widget, method)?.();\n\n return typeof value === \"boolean\" ? value : null;\n};\n\nconst callStringGetter = (widget: Gtk.Widget, method: string): string | null => {\n const value = getCallableMethod<[], unknown>(widget, method)?.();\n\n return typeof value === \"string\" ? value : null;\n};\n\nexport { callBooleanGetter, callStringGetter, getCallableMethod, getWidgetTypeName, hasWidgetMethod };\n"]}
1
+ {"version":3,"file":"widget-getters.js","sourceRoot":"","sources":["../src/widget-getters.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as GObject from \"@gtkx/gi/gobject\";\nimport type * as Gtk from \"@gtkx/gi/gtk\";\nimport { getClassType, getInstanceType, TYPE_INVALID, typeName } from \"@gtkx/runtime\";\nimport { resolveWrapperClass } from \"@gtkx/runtime/internal\";\nimport { type AnyClass, walkClassChain } from \"@gtkx/utils\";\n\nconst UNNAMED_TYPE_TAG = \"Object\";\n\nconst getWidgetMethod = (widget: Gtk.Widget, name: string): unknown => Reflect.get(widget, name);\nconst getWidgetTypeName = (widget: Gtk.Widget): string | null => typeName(getInstanceType(widget));\nconst isClass = (value: unknown): value is AnyClass => typeof value === \"function\";\n\nconst nonEmpty = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.length > 0 ? value : undefined;\n\nconst getObjectClass = (object: GObject.Object): AnyClass | null => {\n const cls: unknown = object.constructor;\n\n return isClass(cls) ? cls : null;\n};\n\nconst getNearestClassName = (object: GObject.Object): string | undefined =>\n walkClassChain(getObjectClass(object), (ancestor) => nonEmpty(ancestor.name));\n\nconst getUntypedClassName = (object: GObject.Object): string | undefined => {\n const cls = getObjectClass(object);\n\n return cls !== null && getClassType(cls) === TYPE_INVALID ? nonEmpty(cls.name) : undefined;\n};\n\nconst getExactWrapperName = (type: bigint): string | undefined => {\n const wrapper = resolveWrapperClass(type);\n\n return wrapper !== null && getClassType(wrapper) === type ? nonEmpty(wrapper.name) : undefined;\n};\n\nconst getTypeTag = (object: GObject.Object): string => {\n const type = getInstanceType(object);\n\n return (\n getUntypedClassName(object) ??\n getExactWrapperName(type) ??\n nonEmpty(typeName(type)) ??\n getNearestClassName(object) ??\n UNNAMED_TYPE_TAG\n );\n};\n\nconst isDefaultWidgetName = (widget: Gtk.Widget, name: string): boolean =>\n name.length === 0 || name === getWidgetTypeName(widget);\n\nconst hasWidgetMethod = (widget: Gtk.Widget, name: string): boolean =>\n typeof getWidgetMethod(widget, name) === \"function\";\n\n/* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */\nconst getCallableMethod = <Args extends unknown[], Result>(\n widget: Gtk.Widget,\n name: string,\n): ((...args: Args) => Result) | null => {\n const fn = getWidgetMethod(widget, name);\n\n return typeof fn === \"function\" ? (fn as (...args: Args) => Result).bind(widget) : null;\n};\n\nconst callBooleanGetter = (widget: Gtk.Widget, method: string): boolean | null => {\n const value = getCallableMethod<[], unknown>(widget, method)?.();\n\n return typeof value === \"boolean\" ? value : null;\n};\n\nconst callStringGetter = (widget: Gtk.Widget, method: string): string | null => {\n const value = getCallableMethod<[], unknown>(widget, method)?.();\n\n return typeof value === \"string\" ? value : null;\n};\n\nexport {\n callBooleanGetter,\n callStringGetter,\n getCallableMethod,\n getTypeTag,\n getWidgetTypeName,\n hasWidgetMethod,\n isDefaultWidgetName,\n};\n"]}
@@ -0,0 +1,10 @@
1
+ import * as Gtk from "@gtkx/gi/gtk";
2
+ declare const mappedToplevels: () => Gtk.Window[];
3
+ declare const isWindowAllocated: (window: Gtk.Window) => boolean;
4
+ declare const activeToplevel: () => Gtk.Window | null;
5
+ declare const isWindowActivated: (window: Gtk.Window) => boolean;
6
+ declare const isWindowBlockedByModal: (window: Gtk.Window) => boolean;
7
+ declare const findPresentedWindowFailure: (window: Gtk.Window) => string | null;
8
+ declare const findRenderedWindowFailure: (window: Gtk.Window) => string | null;
9
+ export { activeToplevel, findPresentedWindowFailure, findRenderedWindowFailure, isWindowActivated, isWindowAllocated, isWindowBlockedByModal, mappedToplevels, };
10
+ //# sourceMappingURL=window-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window-state.d.ts","sourceRoot":"","sources":["../src/window-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AASpC,QAAA,MAAM,eAAe,QAAO,GAAG,CAAC,MAAM,EACoE,CAAC;AAE3G,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,OAI/C,CAAC;AAEF,QAAA,MAAM,cAAc,QAAO,GAAG,CAAC,MAAM,GAAG,IAAyE,CAAC;AAElH,QAAA,MAAM,iBAAiB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,OACa,CAAC;AAqB9D,QAAA,MAAM,sBAAsB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,OAC0B,CAAC;AAEhF,QAAA,MAAM,0BAA0B,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAAM,GAAG,IAMjE,CAAC;AAEF,QAAA,MAAM,yBAAyB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAAM,GAAG,IAMhE,CAAC;AAEF,OAAO,EACH,cAAc,EACd,0BAA0B,EAC1B,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,GAClB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import * as Gtk from "@gtkx/gi/gtk";
2
+ const WINDOW_NOT_ALLOCATED = "it was never allocated a size";
3
+ const WINDOW_NOT_ACTIVATED = "it never became active";
4
+ const NO_WINDOW_ACTIVATED = "no window of the application ever became active";
5
+ const canDisplayDeliverActivation = (window) => window.getDisplay().getDefaultSeat() !== null;
6
+ const isWindow = (widget) => widget instanceof Gtk.Window;
7
+ const mappedToplevels = () => Gtk.Window.listToplevels().filter((widget) => isWindow(widget)).filter((window) => window.getMapped());
8
+ const isWindowAllocated = (window) => {
9
+ const [isComputed, allocation] = window.computeBounds(window);
10
+ return isComputed && allocation.getWidth() > 0;
11
+ };
12
+ const activeToplevel = () => mappedToplevels().find((toplevel) => toplevel.isActive()) ?? null;
13
+ const isWindowActivated = (window) => !canDisplayDeliverActivation(window) || window.isActive();
14
+ const isApplicationActivated = (window) => !canDisplayDeliverActivation(window) || activeToplevel() !== null;
15
+ const isTransientDescendant = (window, ancestor) => {
16
+ for (let parent = window.getTransientFor(); parent !== null; parent = parent.getTransientFor()) {
17
+ if (parent === ancestor) {
18
+ return true;
19
+ }
20
+ }
21
+ return false;
22
+ };
23
+ const isModalGrabHeldOver = (modal, window) => modal.getModal() &&
24
+ modal !== window &&
25
+ modal.getGroup() === window.getGroup() &&
26
+ !isTransientDescendant(window, modal);
27
+ const isWindowBlockedByModal = (window) => mappedToplevels().some((toplevel) => isModalGrabHeldOver(toplevel, window));
28
+ const findPresentedWindowFailure = (window) => {
29
+ if (!isWindowAllocated(window)) {
30
+ return WINDOW_NOT_ALLOCATED;
31
+ }
32
+ return isWindowActivated(window) ? null : WINDOW_NOT_ACTIVATED;
33
+ };
34
+ const findRenderedWindowFailure = (window) => {
35
+ if (!isWindowAllocated(window)) {
36
+ return WINDOW_NOT_ALLOCATED;
37
+ }
38
+ return isApplicationActivated(window) ? null : NO_WINDOW_ACTIVATED;
39
+ };
40
+ export { activeToplevel, findPresentedWindowFailure, findRenderedWindowFailure, isWindowActivated, isWindowAllocated, isWindowBlockedByModal, mappedToplevels, };
41
+ //# sourceMappingURL=window-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window-state.js","sourceRoot":"","sources":["../src/window-state.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\n\nconst WINDOW_NOT_ALLOCATED = \"it was never allocated a size\";\nconst WINDOW_NOT_ACTIVATED = \"it never became active\";\nconst NO_WINDOW_ACTIVATED = \"no window of the application ever became active\";\n\nconst canDisplayDeliverActivation = (window: Gtk.Window): boolean => window.getDisplay().getDefaultSeat() !== null;\nconst isWindow = (widget: Gtk.Widget): widget is Gtk.Window => widget instanceof Gtk.Window;\n\nconst mappedToplevels = (): Gtk.Window[] =>\n Gtk.Window.listToplevels().filter((widget) => isWindow(widget)).filter((window) => window.getMapped());\n\nconst isWindowAllocated = (window: Gtk.Window): boolean => {\n const [isComputed, allocation] = window.computeBounds(window);\n\n return isComputed && allocation.getWidth() > 0;\n};\n\nconst activeToplevel = (): Gtk.Window | null => mappedToplevels().find((toplevel) => toplevel.isActive()) ?? null;\n\nconst isWindowActivated = (window: Gtk.Window): boolean =>\n !canDisplayDeliverActivation(window) || window.isActive();\n\nconst isApplicationActivated = (window: Gtk.Window): boolean =>\n !canDisplayDeliverActivation(window) || activeToplevel() !== null;\n\nconst isTransientDescendant = (window: Gtk.Window, ancestor: Gtk.Window): boolean => {\n for (let parent = window.getTransientFor(); parent !== null; parent = parent.getTransientFor()) {\n if (parent === ancestor) {\n return true;\n }\n }\n\n return false;\n};\n\nconst isModalGrabHeldOver = (modal: Gtk.Window, window: Gtk.Window): boolean =>\n modal.getModal() &&\n modal !== window &&\n modal.getGroup() === window.getGroup() &&\n !isTransientDescendant(window, modal);\n\nconst isWindowBlockedByModal = (window: Gtk.Window): boolean =>\n mappedToplevels().some((toplevel) => isModalGrabHeldOver(toplevel, window));\n\nconst findPresentedWindowFailure = (window: Gtk.Window): string | null => {\n if (!isWindowAllocated(window)) {\n return WINDOW_NOT_ALLOCATED;\n }\n\n return isWindowActivated(window) ? null : WINDOW_NOT_ACTIVATED;\n};\n\nconst findRenderedWindowFailure = (window: Gtk.Window): string | null => {\n if (!isWindowAllocated(window)) {\n return WINDOW_NOT_ALLOCATED;\n }\n\n return isApplicationActivated(window) ? null : NO_WINDOW_ACTIVATED;\n};\n\nexport {\n activeToplevel,\n findPresentedWindowFailure,\n findRenderedWindowFailure,\n isWindowActivated,\n isWindowAllocated,\n isWindowBlockedByModal,\n mappedToplevels,\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/testing",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Testing Library-style queries, user events, and screenshots for GTK4 widget trees rendered by GTKX",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -44,15 +44,25 @@
44
44
  "dist",
45
45
  "src"
46
46
  ],
47
+ "nx": {
48
+ "targets": {
49
+ "lint": {
50
+ "options": {
51
+ "cwd": ".",
52
+ "command": "eslint {projectRoot}"
53
+ }
54
+ }
55
+ }
56
+ },
47
57
  "engines": {
48
58
  "node": ">=24"
49
59
  },
50
60
  "dependencies": {
51
61
  "@vitest/expect": "^4.1.10",
52
62
  "@vitest/pretty-format": "^4.1.10",
53
- "@gtkx/react": "1.0.0",
54
- "@gtkx/utils": "1.0.0",
55
- "@gtkx/runtime": "1.0.0"
63
+ "@gtkx/runtime": "1.2.0",
64
+ "@gtkx/react": "1.2.0",
65
+ "@gtkx/utils": "1.2.0"
56
66
  },
57
67
  "peerDependencies": {
58
68
  "@types/react": "^19.2",
@@ -60,9 +70,7 @@
60
70
  },
61
71
  "devDependencies": {
62
72
  "@types/react": "^19.2.18",
63
- "vitest": "^4.1.10",
64
- "@gtkx/config": "1.0.0",
65
- "@gtkx/vitest": "1.0.0"
73
+ "@gtkx/config": "1.2.0"
66
74
  },
67
75
  "scripts": {
68
76
  "release": "tsx ../../scripts/release-package.ts"
package/src/act.ts CHANGED
@@ -76,4 +76,4 @@ const runInAct = async (callback: () => unknown): Promise<void> => {
76
76
  await act(() => callback());
77
77
  };
78
78
 
79
- export { act, getIsReactActEnvironment, setIsReactActEnvironment, runWithActEnvironment, runInAct };
79
+ export { act, runWithActEnvironment, runInAct };
@@ -187,7 +187,7 @@ const requireSingle =
187
187
  * @param queryAllBy Finds every widget the family matches.
188
188
  * @param getMultipleError Builds the error thrown when a single-match variant finds more than one widget.
189
189
  * @param getMissingError Builds the error thrown when a required match is missing.
190
- * @returns The five variants, in the order `[queryBy, getAllBy, getBy, findAllBy, findBy]`.
190
+ * @returns The variants, in the order `[queryBy, getAllBy, getBy, findAllBy, findBy]`.
191
191
  */
192
192
  const buildQueries = <Args extends unknown[]>(
193
193
  queryAllBy: QueryAllBy<Args>,
package/src/config.ts CHANGED
@@ -15,8 +15,16 @@ type Config = {
15
15
  /** Default timeout in milliseconds for async utilities such as waitFor. */
16
16
  asyncUtilTimeout: number;
17
17
 
18
- /** Timeout in milliseconds for waiting on a widget to become actionable. */
18
+ /** Timeout in milliseconds a user event waits for its widget to become actionable. */
19
19
  actionabilityTimeout: number;
20
+
21
+ /**
22
+ * Timeout in milliseconds `render` waits for the window it shows the tree in to be laid out and
23
+ * activated. A window becoming active is a round trip through the compositor rather than an
24
+ * in-process check, so this is far longer than {@link Config.actionabilityTimeout}: it bounds
25
+ * how long a window that never activates takes to report, and costs nothing when one does.
26
+ */
27
+ windowActivationTimeout: number;
20
28
  };
21
29
 
22
30
  /**
@@ -30,6 +38,7 @@ const defaultConfig: Config = {
30
38
  getElementError: defaultGetElementError,
31
39
  asyncUtilTimeout: 1000,
32
40
  actionabilityTimeout: 500,
41
+ windowActivationTimeout: 5000,
33
42
  };
34
43
 
35
44
  const currentConfig: Config = { ...defaultConfig };