ratatui_ruby 1.5.1-arm64-darwin-23

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 (295) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +15 -0
  3. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  4. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  5. data/LICENSES/CC0-1.0.txt +121 -0
  6. data/LICENSES/LGPL-3.0-or-later.txt +304 -0
  7. data/LICENSES/MIT-0.txt +16 -0
  8. data/LICENSES/MIT.txt +21 -0
  9. data/REUSE.toml +42 -0
  10. data/exe/.gitkeep +0 -0
  11. data/ext/ratatui_ruby/.cargo/config.toml +13 -0
  12. data/ext/ratatui_ruby/.gitignore +4 -0
  13. data/ext/ratatui_ruby/Cargo.lock +1730 -0
  14. data/ext/ratatui_ruby/Cargo.toml +23 -0
  15. data/ext/ratatui_ruby/clippy.toml +7 -0
  16. data/ext/ratatui_ruby/extconf.rb +21 -0
  17. data/ext/ratatui_ruby/src/color.rs +82 -0
  18. data/ext/ratatui_ruby/src/errors.rs +28 -0
  19. data/ext/ratatui_ruby/src/events.rs +706 -0
  20. data/ext/ratatui_ruby/src/frame.rs +241 -0
  21. data/ext/ratatui_ruby/src/lib.rs +343 -0
  22. data/ext/ratatui_ruby/src/lib_header.rs +11 -0
  23. data/ext/ratatui_ruby/src/rendering.rs +158 -0
  24. data/ext/ratatui_ruby/src/string_width.rs +26 -0
  25. data/ext/ratatui_ruby/src/style.rs +469 -0
  26. data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
  27. data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
  28. data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
  29. data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
  30. data/ext/ratatui_ruby/src/terminal/queries.rs +249 -0
  31. data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
  32. data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
  33. data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
  34. data/ext/ratatui_ruby/src/text.rs +225 -0
  35. data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
  36. data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
  37. data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
  38. data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
  39. data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
  40. data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
  41. data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
  42. data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
  43. data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
  44. data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
  45. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
  46. data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
  47. data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
  48. data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
  49. data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
  50. data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
  51. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  52. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
  53. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
  54. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  55. data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
  56. data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
  57. data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
  58. data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
  59. data/lib/ratatui_ruby/3.2/ratatui_ruby.bundle +0 -0
  60. data/lib/ratatui_ruby/3.3/ratatui_ruby.bundle +0 -0
  61. data/lib/ratatui_ruby/3.4/ratatui_ruby.bundle +0 -0
  62. data/lib/ratatui_ruby/4.0/ratatui_ruby.bundle +0 -0
  63. data/lib/ratatui_ruby/backend/window_size.rb +50 -0
  64. data/lib/ratatui_ruby/backend.rb +59 -0
  65. data/lib/ratatui_ruby/buffer/cell.rb +212 -0
  66. data/lib/ratatui_ruby/buffer.rb +149 -0
  67. data/lib/ratatui_ruby/cell.rb +208 -0
  68. data/lib/ratatui_ruby/debug.rb +215 -0
  69. data/lib/ratatui_ruby/draw.rb +63 -0
  70. data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
  71. data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
  72. data/lib/ratatui_ruby/event/key/character.rb +53 -0
  73. data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
  74. data/lib/ratatui_ruby/event/key/media.rb +46 -0
  75. data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
  76. data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
  77. data/lib/ratatui_ruby/event/key/system.rb +47 -0
  78. data/lib/ratatui_ruby/event/key.rb +479 -0
  79. data/lib/ratatui_ruby/event/mouse.rb +291 -0
  80. data/lib/ratatui_ruby/event/none.rb +53 -0
  81. data/lib/ratatui_ruby/event/paste.rb +130 -0
  82. data/lib/ratatui_ruby/event/resize.rb +221 -0
  83. data/lib/ratatui_ruby/event/sync.rb +52 -0
  84. data/lib/ratatui_ruby/event.rb +163 -0
  85. data/lib/ratatui_ruby/frame.rb +257 -0
  86. data/lib/ratatui_ruby/labs/a11y.rb +182 -0
  87. data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
  88. data/lib/ratatui_ruby/labs.rb +47 -0
  89. data/lib/ratatui_ruby/layout/alignment.rb +91 -0
  90. data/lib/ratatui_ruby/layout/constraint.rb +337 -0
  91. data/lib/ratatui_ruby/layout/layout.rb +258 -0
  92. data/lib/ratatui_ruby/layout/position.rb +81 -0
  93. data/lib/ratatui_ruby/layout/rect.rb +733 -0
  94. data/lib/ratatui_ruby/layout/size.rb +62 -0
  95. data/lib/ratatui_ruby/layout.rb +29 -0
  96. data/lib/ratatui_ruby/list_state.rb +201 -0
  97. data/lib/ratatui_ruby/output_guard.rb +171 -0
  98. data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
  99. data/lib/ratatui_ruby/style/color.rb +149 -0
  100. data/lib/ratatui_ruby/style/style.rb +147 -0
  101. data/lib/ratatui_ruby/style.rb +19 -0
  102. data/lib/ratatui_ruby/symbols.rb +435 -0
  103. data/lib/ratatui_ruby/synthetic_events.rb +106 -0
  104. data/lib/ratatui_ruby/table_state.rb +251 -0
  105. data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
  106. data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
  107. data/lib/ratatui_ruby/terminal.rb +66 -0
  108. data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
  109. data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
  110. data/lib/ratatui_ruby/test_helper/event_injection.rb +244 -0
  111. data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
  112. data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
  113. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
  114. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
  115. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
  116. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
  117. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
  118. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
  119. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
  120. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
  121. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
  122. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
  123. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
  124. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
  125. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
  126. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
  127. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
  128. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
  129. data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
  130. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
  131. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
  132. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
  133. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
  134. data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
  135. data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
  136. data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
  137. data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
  138. data/lib/ratatui_ruby/test_helper.rb +115 -0
  139. data/lib/ratatui_ruby/text/line.rb +245 -0
  140. data/lib/ratatui_ruby/text/span.rb +158 -0
  141. data/lib/ratatui_ruby/text.rb +99 -0
  142. data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
  143. data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
  144. data/lib/ratatui_ruby/tui/core.rb +67 -0
  145. data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
  146. data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
  147. data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
  148. data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
  149. data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
  150. data/lib/ratatui_ruby/tui.rb +106 -0
  151. data/lib/ratatui_ruby/version.rb +12 -0
  152. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
  153. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
  154. data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
  155. data/lib/ratatui_ruby/widgets/block.rb +266 -0
  156. data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
  157. data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
  158. data/lib/ratatui_ruby/widgets/cell.rb +59 -0
  159. data/lib/ratatui_ruby/widgets/center.rb +71 -0
  160. data/lib/ratatui_ruby/widgets/chart.rb +172 -0
  161. data/lib/ratatui_ruby/widgets/clear.rb +66 -0
  162. data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
  163. data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
  164. data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
  165. data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
  166. data/lib/ratatui_ruby/widgets/list.rb +252 -0
  167. data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
  168. data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
  169. data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
  170. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
  171. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
  172. data/lib/ratatui_ruby/widgets/row.rb +123 -0
  173. data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
  174. data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
  175. data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
  176. data/lib/ratatui_ruby/widgets/table.rb +213 -0
  177. data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
  178. data/lib/ratatui_ruby/widgets.rb +43 -0
  179. data/lib/ratatui_ruby.rb +556 -0
  180. data/sig/examples/app_all_events/app.rbs +11 -0
  181. data/sig/examples/app_all_events/model/app_model.rbs +23 -0
  182. data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
  183. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  184. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  185. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  186. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  187. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  188. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  189. data/sig/examples/app_all_events/view.rbs +14 -0
  190. data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
  191. data/sig/examples/app_color_picker/app.rbs +17 -0
  192. data/sig/examples/app_external_editor/app.rbs +12 -0
  193. data/sig/examples/app_login_form/app.rbs +11 -0
  194. data/sig/examples/app_stateful_interaction/app.rbs +39 -0
  195. data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
  196. data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
  197. data/sig/examples/verify_readme_usage/app.rbs +17 -0
  198. data/sig/examples/widget_block_demo/app.rbs +38 -0
  199. data/sig/examples/widget_box_demo/app.rbs +17 -0
  200. data/sig/examples/widget_calendar_demo/app.rbs +17 -0
  201. data/sig/examples/widget_cell_demo/app.rbs +17 -0
  202. data/sig/examples/widget_chart_demo/app.rbs +17 -0
  203. data/sig/examples/widget_gauge_demo/app.rbs +17 -0
  204. data/sig/examples/widget_layout_split/app.rbs +16 -0
  205. data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
  206. data/sig/examples/widget_list_demo/app.rbs +17 -0
  207. data/sig/examples/widget_map_demo/app.rbs +17 -0
  208. data/sig/examples/widget_popup_demo/app.rbs +17 -0
  209. data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
  210. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
  211. data/sig/examples/widget_rect/app.rbs +18 -0
  212. data/sig/examples/widget_render/app.rbs +16 -0
  213. data/sig/examples/widget_rich_text/app.rbs +17 -0
  214. data/sig/examples/widget_scroll_text/app.rbs +17 -0
  215. data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
  216. data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
  217. data/sig/examples/widget_style_colors/app.rbs +20 -0
  218. data/sig/examples/widget_table_demo/app.rbs +17 -0
  219. data/sig/examples/widget_text_width/app.rbs +16 -0
  220. data/sig/generated/event_key_predicates.rbs +1348 -0
  221. data/sig/manifest.yaml +5 -0
  222. data/sig/patches/data.rbs +26 -0
  223. data/sig/patches/debugger__.rbs +8 -0
  224. data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
  225. data/sig/ratatui_ruby/backend.rbs +12 -0
  226. data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
  227. data/sig/ratatui_ruby/buffer.rbs +18 -0
  228. data/sig/ratatui_ruby/cell.rbs +44 -0
  229. data/sig/ratatui_ruby/clear.rbs +18 -0
  230. data/sig/ratatui_ruby/constraint.rbs +26 -0
  231. data/sig/ratatui_ruby/debug.rbs +45 -0
  232. data/sig/ratatui_ruby/draw.rbs +30 -0
  233. data/sig/ratatui_ruby/event.rbs +249 -0
  234. data/sig/ratatui_ruby/frame.rbs +23 -0
  235. data/sig/ratatui_ruby/interfaces.rbs +25 -0
  236. data/sig/ratatui_ruby/labs.rbs +90 -0
  237. data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
  238. data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
  239. data/sig/ratatui_ruby/layout/layout.rbs +45 -0
  240. data/sig/ratatui_ruby/layout/position.rbs +18 -0
  241. data/sig/ratatui_ruby/layout/rect.rbs +64 -0
  242. data/sig/ratatui_ruby/layout/size.rbs +18 -0
  243. data/sig/ratatui_ruby/list_state.rbs +23 -0
  244. data/sig/ratatui_ruby/output_guard.rbs +23 -0
  245. data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
  246. data/sig/ratatui_ruby/rect.rbs +17 -0
  247. data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
  248. data/sig/ratatui_ruby/session.rbs +93 -0
  249. data/sig/ratatui_ruby/style/color.rbs +22 -0
  250. data/sig/ratatui_ruby/style/style.rbs +29 -0
  251. data/sig/ratatui_ruby/symbols.rbs +141 -0
  252. data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
  253. data/sig/ratatui_ruby/table_state.rbs +27 -0
  254. data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
  255. data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
  256. data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
  257. data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
  258. data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
  259. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
  260. data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
  261. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
  262. data/sig/ratatui_ruby/test_helper.rbs +18 -0
  263. data/sig/ratatui_ruby/text/line.rbs +27 -0
  264. data/sig/ratatui_ruby/text/span.rbs +23 -0
  265. data/sig/ratatui_ruby/text.rbs +12 -0
  266. data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
  267. data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
  268. data/sig/ratatui_ruby/tui/core.rbs +23 -0
  269. data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
  270. data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
  271. data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
  272. data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
  273. data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
  274. data/sig/ratatui_ruby/tui.rbs +25 -0
  275. data/sig/ratatui_ruby/version.rbs +12 -0
  276. data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
  277. data/sig/ratatui_ruby/widgets/block.rbs +51 -0
  278. data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
  279. data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
  280. data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
  281. data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
  282. data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
  283. data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
  284. data/sig/ratatui_ruby/widgets/list.rbs +63 -0
  285. data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
  286. data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
  287. data/sig/ratatui_ruby/widgets/row.rbs +43 -0
  288. data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
  289. data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
  290. data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
  291. data/sig/ratatui_ruby/widgets/table.rbs +78 -0
  292. data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
  293. data/sig/ratatui_ruby/widgets.rbs +16 -0
  294. data/vendor/goodcop/base.yml +1047 -0
  295. metadata +738 -0
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module TestHelper
10
+ ##
11
+ # Event injection helpers for testing TUI interactions.
12
+ #
13
+ # Testing keyboard navigation and mouse clicks requires simulating user input.
14
+ # Constructing event objects by hand for every test is verbose and repetitive.
15
+ #
16
+ # This mixin provides convenience methods to inject keys, clicks, and other events
17
+ # into the test terminal's event queue. Events are consumed by the next
18
+ # <tt>poll_event</tt> call.
19
+ #
20
+ # Use it to simulate user interactions: typing, clicking, dragging, pasting.
21
+ #
22
+ # === Examples
23
+ #
24
+ #--
25
+ # SPDX-SnippetBegin
26
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
27
+ # SPDX-License-Identifier: MIT-0
28
+ #++
29
+ # with_test_terminal do
30
+ # inject_keys("h", "e", "l", "l", "o")
31
+ # inject_keys(:enter, :ctrl_s)
32
+ # inject_click(x: 10, y: 5)
33
+ # inject_event(RatatuiRuby::Event::Paste.new(content: "pasted text"))
34
+ #
35
+ # @app.run
36
+ # end
37
+ #
38
+ #--
39
+ # SPDX-SnippetEnd
40
+ #++
41
+ module EventInjection
42
+ ##
43
+ # Injects an event into the test terminal's event queue.
44
+ #
45
+ # Pass any <tt>RatatuiRuby::Event</tt> object. The event is returned by
46
+ # the next <tt>poll_event</tt> call.
47
+ #
48
+ # Raises <tt>RuntimeError</tt> if called outside a <tt>with_test_terminal</tt> block.
49
+ #
50
+ # === Examples
51
+ #
52
+ #--
53
+ # SPDX-SnippetBegin
54
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
55
+ # SPDX-License-Identifier: MIT-0
56
+ #++
57
+ # inject_event(RatatuiRuby::Event::Key.new(code: "q"))
58
+ # inject_event(RatatuiRuby::Event::Mouse.new(kind: "down", button: "left", x: 10, y: 5))
59
+ # inject_event(RatatuiRuby::Event::Paste.new(content: "Hello"))
60
+ # inject_event(RatatuiRuby::Event::None.new) # idle frame
61
+ #
62
+ #--
63
+ # SPDX-SnippetEnd
64
+ #++
65
+ # [event] A <tt>RatatuiRuby::Event</tt> object.
66
+ def inject_event(event)
67
+ unless @_ratatui_test_terminal_active
68
+ raise RatatuiRuby::Error::Invariant, "Events must be injected inside a `with_test_terminal` block. " \
69
+ "Calling this method outside the block causes a race condition where the event " \
70
+ "is flushed before the application starts."
71
+ end
72
+
73
+ case event
74
+ when RatatuiRuby::Event::Key
75
+ RatatuiRuby.inject_test_event("key", { code: event.code, modifiers: event.modifiers })
76
+ when RatatuiRuby::Event::Mouse
77
+ RatatuiRuby.inject_test_event("mouse", {
78
+ kind: event.kind,
79
+ button: event.button,
80
+ x: event.x,
81
+ y: event.y,
82
+ modifiers: event.modifiers,
83
+ })
84
+ when RatatuiRuby::Event::Resize
85
+ RatatuiRuby.inject_test_event("resize", { width: event.width, height: event.height })
86
+ when RatatuiRuby::Event::Paste
87
+ RatatuiRuby.inject_test_event("paste", { content: event.content })
88
+ when RatatuiRuby::Event::FocusGained
89
+ RatatuiRuby.inject_test_event("focus_gained", {})
90
+ when RatatuiRuby::Event::FocusLost
91
+ RatatuiRuby.inject_test_event("focus_lost", {})
92
+ when RatatuiRuby::Event::Sync
93
+ if RatatuiRuby::SyntheticEvents.inline_sync?
94
+ # Route through native queue for deterministic ordering with key events
95
+ RatatuiRuby.inject_test_event("sync", {})
96
+ else
97
+ # Default 1.0 behavior: use the engine-level synthetic queue
98
+ RatatuiRuby::SyntheticEvents.push(event)
99
+ end
100
+ when RatatuiRuby::Event::None
101
+ RatatuiRuby.inject_test_event("none", {})
102
+ else
103
+ raise ArgumentError, "Unknown event type: #{event.class}"
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Injects a mouse event.
109
+ #
110
+ # === Example
111
+ #
112
+ #--
113
+ # SPDX-SnippetBegin
114
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
115
+ # SPDX-License-Identifier: MIT-0
116
+ #++
117
+ # inject_mouse(x: 10, y: 5, kind: :down, button: :left)
118
+ #
119
+ #--
120
+ # SPDX-SnippetEnd
121
+ #++
122
+ # [x] Integer x-coordinate.
123
+ # [y] Integer y-coordinate.
124
+ # [kind] Symbol <tt>:down</tt>, <tt>:up</tt>, or <tt>:drag</tt>.
125
+ # [button] Symbol <tt>:left</tt>, <tt>:right</tt>, or <tt>:middle</tt>.
126
+ # [modifiers] Array of modifier strings.
127
+ def inject_mouse(x:, y:, kind: :down, modifiers: [], button: :left)
128
+ event = RatatuiRuby::Event::Mouse.new(
129
+ kind: kind.to_s,
130
+ x:,
131
+ y:,
132
+ button: button.to_s,
133
+ modifiers:
134
+ )
135
+ inject_event(event)
136
+ end
137
+
138
+ ##
139
+ # Injects a left mouse click.
140
+ #
141
+ # === Example
142
+ #
143
+ # inject_click(x: 10, y: 5)
144
+ def inject_click(x:, y:, modifiers: [])
145
+ inject_mouse(x:, y:, kind: :down, modifiers:, button: :left)
146
+ end
147
+
148
+ ##
149
+ # Injects a right mouse click.
150
+ #
151
+ # === Example
152
+ #
153
+ # inject_right_click(x: 10, y: 5)
154
+ def inject_right_click(x:, y:, modifiers: [])
155
+ inject_mouse(x:, y:, kind: :down, modifiers:, button: :right)
156
+ end
157
+
158
+ ##
159
+ # Injects a mouse drag event.
160
+ #
161
+ # === Example
162
+ #
163
+ # inject_drag(x: 10, y: 5)
164
+ def inject_drag(x:, y:, modifiers: [], button: :left)
165
+ inject_mouse(x:, y:, kind: :drag, modifiers:, button:)
166
+ end
167
+
168
+ ##
169
+ # Injects one or more key events.
170
+ #
171
+ # Accepts multiple formats for convenience:
172
+ # - String: Character key (e.g., <tt>"a"</tt>, <tt>"q"</tt>)
173
+ # - Symbol: Named key or modifier combo (e.g., <tt>:enter</tt>, <tt>:ctrl_c</tt>)
174
+ # - Hash: Passed to <tt>Key.new</tt>
175
+ # - Key: Passed directly
176
+ #
177
+ # === Examples
178
+ #
179
+ #--
180
+ # SPDX-SnippetBegin
181
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
182
+ # SPDX-License-Identifier: MIT-0
183
+ #++
184
+ # inject_keys("a", "b", "c")
185
+ # inject_keys(:enter, :esc)
186
+ # inject_keys(:ctrl_c, :alt_shift_left)
187
+ # inject_keys("j", { code: "k", modifiers: ["ctrl"] })
188
+ #--
189
+ # SPDX-SnippetEnd
190
+ #++
191
+ def inject_keys(*args)
192
+ args.each do |arg|
193
+ event = case arg
194
+ when String
195
+ RatatuiRuby::Event::Key.new(code: arg)
196
+ when Symbol
197
+ parts = arg.to_s.split("_")
198
+ code = parts.pop
199
+ modifiers = parts
200
+ RatatuiRuby::Event::Key.new(code:, modifiers:)
201
+ when Hash
202
+ RatatuiRuby::Event::Key.new(**arg)
203
+ when RatatuiRuby::Event::Key
204
+ arg
205
+ else
206
+ raise ArgumentError, "Invalid key argument: #{arg.inspect}. Expected String, Symbol, Hash, or Key event."
207
+ end
208
+ inject_event(event)
209
+ end
210
+ end
211
+ alias inject_key inject_keys
212
+
213
+ ##
214
+ # Injects a Sync event.
215
+ #
216
+ # When a runtime (Tea, Kit) encounters this event, it should wait for all
217
+ # pending async operations to complete before processing the next event.
218
+ # This enables deterministic testing of async behavior.
219
+ #
220
+ # *Important*: Sync waits for commands to _complete_. Do not use it
221
+ # with long-running commands that wait indefinitely (e.g., for
222
+ # cancellation). Those commands will block forever, causing a timeout.
223
+ # For cancellation tests, dispatch the cancel command without Sync.
224
+ #
225
+ # === Example
226
+ #
227
+ #--
228
+ # SPDX-SnippetBegin
229
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
230
+ # SPDX-License-Identifier: MIT-0
231
+ #++
232
+ # inject_key("s") # Triggers async command
233
+ # inject_sync # Wait for command to complete
234
+ # inject_key(:q) # Quit after seeing results
235
+ #
236
+ #--
237
+ # SPDX-SnippetEnd
238
+ #++
239
+ def inject_sync
240
+ inject_event(RatatuiRuby::Event::Sync.new)
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long &lt;me@kerricklong.com&gt;
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module TestHelper
10
+ ##
11
+ # Helpers for testing code that reads global state.
12
+ #
13
+ # Applications often read +ARGV+ and +ENV+ at startup. Testing these code
14
+ # paths requires stubbing those constants. Doing it manually is brittle —
15
+ # tests that forget to restore the original values leak state.
16
+ #
17
+ # These helpers swap the constants, yield to the block, and restore the
18
+ # originals automatically.
19
+ #
20
+ # == Example
21
+ #
22
+ #--
23
+ # SPDX-SnippetBegin
24
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
25
+ # SPDX-License-Identifier: MIT-0
26
+ #++
27
+ # def test_parses_command_line_flags
28
+ # with_argv(["--verbose", "--log=debug.log"]) do
29
+ # app = MyApp.new
30
+ # assert app.verbose?
31
+ # assert_equal "debug.log", app.log_path
32
+ # end
33
+ # end
34
+ #
35
+ # def test_reads_api_key_from_environment
36
+ # with_env("API_KEY", "test-key-1234") do
37
+ # client = APIClient.new
38
+ # assert_equal "test-key-1234", client.api_key
39
+ # end
40
+ # end
41
+ #--
42
+ # SPDX-SnippetEnd
43
+ #++
44
+ module GlobalState
45
+ # Temporarily replaces ARGV for the duration of the block.
46
+ #
47
+ # [argv] An array of strings to use as ARGV.
48
+ #
49
+ # === Example
50
+ #
51
+ #--
52
+ # SPDX-SnippetBegin
53
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
54
+ # SPDX-License-Identifier: MIT-0
55
+ #++
56
+ # with_argv(["--help"]) do
57
+ # # Code here sees ARGV as ["--help"]
58
+ # end
59
+ #--
60
+ # SPDX-SnippetEnd
61
+ #++
62
+ def with_argv(argv)
63
+ original_argv = ARGV.dup
64
+ ARGV.replace(argv)
65
+ yield
66
+ ensure
67
+ ARGV.replace(original_argv)
68
+ end
69
+
70
+ # Temporarily replaces an environment variable for the duration of the block.
71
+ #
72
+ # If +value+ is +nil+, the variable is deleted for the block's duration.
73
+ #
74
+ # [key] The environment variable name (String).
75
+ # [value] The value to set, or +nil+ to delete.
76
+ #
77
+ # === Example
78
+ #
79
+ #--
80
+ # SPDX-SnippetBegin
81
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
82
+ # SPDX-License-Identifier: MIT-0
83
+ #++
84
+ # with_env("DEBUG", "1") do
85
+ # # Code here sees ENV["DEBUG"] as "1"
86
+ # end
87
+ #
88
+ # with_env("DEBUG", nil) do
89
+ # # Code here does not see ENV["DEBUG"]
90
+ # end
91
+ #--
92
+ # SPDX-SnippetEnd
93
+ #++
94
+ def with_env(key, value)
95
+ original_value = ENV[key]
96
+ if value.nil?
97
+ ENV.delete(key)
98
+ else
99
+ ENV[key] = value
100
+ end
101
+ yield
102
+ ensure
103
+ if original_value.nil?
104
+ ENV.delete(key)
105
+ else
106
+ ENV[key] = original_value
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end