teek-ui 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +41 -0
- data/README.md +452 -0
- data/lib/teek/ui/canvas_item.rb +270 -0
- data/lib/teek/ui/component_handle.rb +32 -0
- data/lib/teek/ui/document.rb +185 -0
- data/lib/teek/ui/errors.rb +41 -0
- data/lib/teek/ui/event_binding.rb +22 -0
- data/lib/teek/ui/event_bus.rb +53 -0
- data/lib/teek/ui/grid_validator.rb +89 -0
- data/lib/teek/ui/handle.rb +585 -0
- data/lib/teek/ui/image.rb +58 -0
- data/lib/teek/ui/keysyms.rb +72 -0
- data/lib/teek/ui/menu_builder.rb +130 -0
- data/lib/teek/ui/menu_entry_addressing.rb +62 -0
- data/lib/teek/ui/modal_stack.rb +99 -0
- data/lib/teek/ui/mouse_events.rb +31 -0
- data/lib/teek/ui/node.rb +141 -0
- data/lib/teek/ui/option_dump_parsing.rb +31 -0
- data/lib/teek/ui/overlay_anchors.rb +29 -0
- data/lib/teek/ui/overlay_validator.rb +33 -0
- data/lib/teek/ui/pane_validator.rb +30 -0
- data/lib/teek/ui/realized_node.rb +25 -0
- data/lib/teek/ui/realizer.rb +577 -0
- data/lib/teek/ui/scope.rb +40 -0
- data/lib/teek/ui/screens.rb +141 -0
- data/lib/teek/ui/session.rb +417 -0
- data/lib/teek/ui/tab_validator.rb +30 -0
- data/lib/teek/ui/text_content.rb +321 -0
- data/lib/teek/ui/tree_inspector.rb +97 -0
- data/lib/teek/ui/validator.rb +131 -0
- data/lib/teek/ui/var.rb +98 -0
- data/lib/teek/ui/version.rb +7 -0
- data/lib/teek/ui/widget_addressing.rb +47 -0
- data/lib/teek/ui/widget_dsl.rb +512 -0
- data/lib/teek/ui/widget_type.rb +242 -0
- data/lib/teek/ui/widget_types/button.rb +7 -0
- data/lib/teek/ui/widget_types/canvas.rb +15 -0
- data/lib/teek/ui/widget_types/checkbox.rb +7 -0
- data/lib/teek/ui/widget_types/column.rb +14 -0
- data/lib/teek/ui/widget_types/context_menu.rb +15 -0
- data/lib/teek/ui/widget_types/divider.rb +10 -0
- data/lib/teek/ui/widget_types/dropdown.rb +7 -0
- data/lib/teek/ui/widget_types/grid.rb +12 -0
- data/lib/teek/ui/widget_types/group.rb +7 -0
- data/lib/teek/ui/widget_types/label.rb +7 -0
- data/lib/teek/ui/widget_types/list.rb +7 -0
- data/lib/teek/ui/widget_types/menu_bar.rb +26 -0
- data/lib/teek/ui/widget_types/menu_checkbox.rb +14 -0
- data/lib/teek/ui/widget_types/menu_item.rb +20 -0
- data/lib/teek/ui/widget_types/menu_radio.rb +14 -0
- data/lib/teek/ui/widget_types/number_box.rb +7 -0
- data/lib/teek/ui/widget_types/pane.rb +37 -0
- data/lib/teek/ui/widget_types/panel.rb +7 -0
- data/lib/teek/ui/widget_types/progress.rb +7 -0
- data/lib/teek/ui/widget_types/radio.rb +7 -0
- data/lib/teek/ui/widget_types/row.rb +14 -0
- data/lib/teek/ui/widget_types/scrollable.rb +17 -0
- data/lib/teek/ui/widget_types/slider.rb +7 -0
- data/lib/teek/ui/widget_types/spacer.rb +14 -0
- data/lib/teek/ui/widget_types/split.rb +17 -0
- data/lib/teek/ui/widget_types/tab.rb +35 -0
- data/lib/teek/ui/widget_types/table.rb +9 -0
- data/lib/teek/ui/widget_types/tabs.rb +10 -0
- data/lib/teek/ui/widget_types/text_area.rb +7 -0
- data/lib/teek/ui/widget_types/text_box.rb +7 -0
- data/lib/teek/ui/widget_types/tree.rb +9 -0
- data/lib/teek/ui/widget_types/window.rb +50 -0
- data/lib/teek/ui/widget_types.rb +121 -0
- data/lib/teek/ui/widget_validators.rb +62 -0
- data/lib/teek/ui.rb +54 -0
- data/teek-ui.gemspec +24 -0
- data/test/support/fake_app.rb +57 -0
- data/test/test_busy.rb +82 -0
- data/test/test_canvas_items.rb +551 -0
- data/test/test_clipboard.rb +87 -0
- data/test/test_close_handling.rb +118 -0
- data/test/test_component.rb +151 -0
- data/test/test_component_realtk.rb +120 -0
- data/test/test_debug_info.rb +158 -0
- data/test/test_document.rb +317 -0
- data/test/test_event_bus.rb +163 -0
- data/test/test_events.rb +164 -0
- data/test/test_fake_app_contract.rb +62 -0
- data/test/test_grid.rb +97 -0
- data/test/test_handle.rb +386 -0
- data/test/test_handle_destroy_realtk.rb +344 -0
- data/test/test_helper.rb +20 -0
- data/test/test_image.rb +24 -0
- data/test/test_image_realtk.rb +146 -0
- data/test/test_incremental_realize.rb +203 -0
- data/test/test_keysyms.rb +76 -0
- data/test/test_layout.rb +185 -0
- data/test/test_lazy_realize.rb +128 -0
- data/test/test_managed_window.rb +304 -0
- data/test/test_menu_dsl.rb +230 -0
- data/test/test_menu_entry_addressing.rb +73 -0
- data/test/test_menu_realize.rb +296 -0
- data/test/test_modal_handle.rb +147 -0
- data/test/test_modal_stack.rb +219 -0
- data/test/test_modal_stack_realtk.rb +131 -0
- data/test/test_native_scrollable.rb +250 -0
- data/test/test_node.rb +193 -0
- data/test/test_overlay.rb +139 -0
- data/test/test_raw.rb +58 -0
- data/test/test_reactive_vars.rb +118 -0
- data/test/test_realizer.rb +264 -0
- data/test/test_scope.rb +38 -0
- data/test/test_screens.rb +260 -0
- data/test/test_screens_realtk.rb +314 -0
- data/test/test_scrollable.rb +185 -0
- data/test/test_scrollable_wheel.rb +172 -0
- data/test/test_scrollbar_auto_hide.rb +127 -0
- data/test/test_split.rb +145 -0
- data/test/test_tabs.rb +201 -0
- data/test/test_text_content.rb +664 -0
- data/test/test_toast.rb +126 -0
- data/test/test_tree_inspector.rb +183 -0
- data/test/test_ui.rb +428 -0
- data/test/test_validator.rb +209 -0
- data/test/test_var.rb +32 -0
- data/test/test_widget_dsl.rb +621 -0
- data/test/test_widget_introspection.rb +126 -0
- data/test/test_widget_types.rb +429 -0
- data/test/test_widget_types_realtk.rb +82 -0
- data/test/test_widget_validators.rb +82 -0
- metadata +207 -0
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
require 'teek/ui/session'
|
|
5
|
+
|
|
6
|
+
class TestWidgetDsl < Minitest::Test
|
|
7
|
+
LEAF_WIDGET_TYPES = %i[
|
|
8
|
+
text_box text_area label button checkbox radio slider dropdown
|
|
9
|
+
number_box list table tree progress divider
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
def build_session
|
|
13
|
+
Teek::UI::Session.new(title: 'Widget DSL Test')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_each_leaf_widget_method_appends_a_node_of_the_matching_type
|
|
17
|
+
LEAF_WIDGET_TYPES.each do |method|
|
|
18
|
+
session = build_session
|
|
19
|
+
|
|
20
|
+
handle = session.send(method, :w, text: 'x')
|
|
21
|
+
|
|
22
|
+
node = session.document.root.children.first
|
|
23
|
+
assert_equal method, node.type, "##{method} should create a :#{method} node"
|
|
24
|
+
assert_equal({ text: 'x' }, node.opts)
|
|
25
|
+
assert_kind_of Teek::UI::Handle, handle
|
|
26
|
+
assert_equal method, handle.type
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_leaf_widgets_work_unnamed
|
|
31
|
+
session = build_session
|
|
32
|
+
|
|
33
|
+
session.label(text: 'Hi')
|
|
34
|
+
|
|
35
|
+
node = session.document.root.children.first
|
|
36
|
+
assert_equal :label, node.type
|
|
37
|
+
assert_nil node.name
|
|
38
|
+
refute_nil node.key
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_named_widget_is_addressable_via_bracket_lookup
|
|
42
|
+
session = build_session
|
|
43
|
+
|
|
44
|
+
session.text_box(:query)
|
|
45
|
+
|
|
46
|
+
handle = session[:query]
|
|
47
|
+
assert_kind_of Teek::UI::Handle, handle
|
|
48
|
+
assert_equal :text_box, handle.type
|
|
49
|
+
assert_equal :query, handle.name
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_bracket_lookup_returns_nil_for_an_unknown_name
|
|
53
|
+
session = build_session
|
|
54
|
+
|
|
55
|
+
assert_nil session[:nope]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_duplicate_name_raises_through_the_dsl
|
|
59
|
+
session = build_session
|
|
60
|
+
session.button(:save)
|
|
61
|
+
|
|
62
|
+
assert_raises(ArgumentError) { session.button(:save) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_panel_nests_children_declared_in_its_block
|
|
66
|
+
session = build_session
|
|
67
|
+
|
|
68
|
+
session.panel(:controls) do |p|
|
|
69
|
+
p.button(:go, text: 'Go')
|
|
70
|
+
p.button(:stop, text: 'Stop')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
panel_node = session.document.root.children.first
|
|
74
|
+
assert_equal :panel, panel_node.type
|
|
75
|
+
assert_equal [:button, :button], panel_node.children.map(&:type)
|
|
76
|
+
assert_equal [:go, :stop], panel_node.children.map(&:name)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_container_block_yields_the_same_session_object
|
|
80
|
+
session = build_session
|
|
81
|
+
yielded = nil
|
|
82
|
+
|
|
83
|
+
session.panel(:controls) { |p| yielded = p }
|
|
84
|
+
|
|
85
|
+
# not a separate scoped builder - the same object, so a name declared
|
|
86
|
+
# inside the block is addressable from outside it too via ui[:name]
|
|
87
|
+
assert_same session, yielded
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_nested_containers_attach_at_the_correct_depth
|
|
91
|
+
session = build_session
|
|
92
|
+
|
|
93
|
+
session.panel(:outer) do |outer|
|
|
94
|
+
outer.panel(:inner) do |inner|
|
|
95
|
+
inner.button(:deep)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
outer_node = session.document.root.children.first
|
|
100
|
+
inner_node = outer_node.children.first
|
|
101
|
+
deep_node = inner_node.children.first
|
|
102
|
+
|
|
103
|
+
assert_equal :panel, outer_node.type
|
|
104
|
+
assert_equal :panel, inner_node.type
|
|
105
|
+
assert_equal :button, deep_node.type
|
|
106
|
+
assert_equal [], deep_node.children
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_a_container_without_a_block_still_creates_a_childless_node
|
|
110
|
+
session = build_session
|
|
111
|
+
|
|
112
|
+
session.window(:settings, title: 'Settings')
|
|
113
|
+
|
|
114
|
+
node = session.document.root.children.first
|
|
115
|
+
assert_equal :window, node.type
|
|
116
|
+
assert_equal [], node.children
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_box_is_a_synonym_for_panel
|
|
120
|
+
session = build_session
|
|
121
|
+
|
|
122
|
+
session.box(:sidebar)
|
|
123
|
+
|
|
124
|
+
node = session.document.root.children.first
|
|
125
|
+
assert_equal :panel, node.type
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
%i[group canvas window].each do |container|
|
|
129
|
+
define_method("test_#{container}_is_a_container_that_nests_children") do
|
|
130
|
+
session = build_session
|
|
131
|
+
|
|
132
|
+
session.send(container, :c) { |b| b.label(:inner_label) }
|
|
133
|
+
|
|
134
|
+
node = session.document.root.children.first
|
|
135
|
+
assert_equal container, node.type
|
|
136
|
+
assert_equal [:label], node.children.map(&:type)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_var_is_tracked_on_the_session
|
|
141
|
+
session = build_session
|
|
142
|
+
|
|
143
|
+
speed = session.var(5)
|
|
144
|
+
|
|
145
|
+
assert_kind_of Teek::UI::Var, speed
|
|
146
|
+
assert_includes session.vars, speed
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_var_names_are_unique_within_a_session
|
|
150
|
+
session = build_session
|
|
151
|
+
|
|
152
|
+
a = session.var(1)
|
|
153
|
+
b = session.var(2)
|
|
154
|
+
|
|
155
|
+
refute_equal a.name, b.name
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def test_bind_translates_to_the_variable_option_for_a_slider
|
|
159
|
+
session = build_session
|
|
160
|
+
speed = session.var(5)
|
|
161
|
+
|
|
162
|
+
session.slider(:s, from: 1, to: 10, bind: speed)
|
|
163
|
+
|
|
164
|
+
node = session.document.root.children.first
|
|
165
|
+
assert_equal speed.name, node.opts[:variable]
|
|
166
|
+
refute node.opts.key?(:bind), "bind: should not leak through to the realized widget options"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_bind_translates_to_the_textvariable_option_for_a_text_box
|
|
170
|
+
session = build_session
|
|
171
|
+
speed = session.var(5)
|
|
172
|
+
|
|
173
|
+
session.text_box(:t, bind: speed)
|
|
174
|
+
|
|
175
|
+
node = session.document.root.children.first
|
|
176
|
+
assert_equal speed.name, node.opts[:textvariable]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_image_is_tracked_on_the_session
|
|
180
|
+
session = build_session
|
|
181
|
+
|
|
182
|
+
icon = session.image('/tmp/whatever.png')
|
|
183
|
+
|
|
184
|
+
assert_kind_of Teek::UI::Image, icon
|
|
185
|
+
assert_includes session.images, icon
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def test_image_names_are_unique_within_a_session
|
|
189
|
+
session = build_session
|
|
190
|
+
|
|
191
|
+
a = session.image('/tmp/a.png')
|
|
192
|
+
b = session.image('/tmp/b.png')
|
|
193
|
+
|
|
194
|
+
refute_equal a.name, b.name
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def test_a_widgets_image_option_holds_the_declared_image_directly
|
|
198
|
+
session = build_session
|
|
199
|
+
icon = session.image('/tmp/whatever.png')
|
|
200
|
+
|
|
201
|
+
session.label(:pic, image: icon)
|
|
202
|
+
|
|
203
|
+
node = session.document.root.children.first
|
|
204
|
+
assert_same icon, node.opts[:image]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def test_bind_on_an_unsupported_widget_type_raises
|
|
208
|
+
session = build_session
|
|
209
|
+
speed = session.var(5)
|
|
210
|
+
|
|
211
|
+
assert_raises(ArgumentError) { session.divider(:d, bind: speed) }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def test_scroll_on_an_unsupported_widget_type_raises
|
|
215
|
+
session = build_session
|
|
216
|
+
|
|
217
|
+
error = assert_raises(ArgumentError) { session.button(:go, scroll: true) }
|
|
218
|
+
assert_match(/scroll:/, error.message)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def test_scroll_on_a_natively_scrollable_widget_type_does_not_raise
|
|
222
|
+
session = build_session
|
|
223
|
+
|
|
224
|
+
session.list(:items, scroll: false)
|
|
225
|
+
session.canvas(:board, scroll: true)
|
|
226
|
+
|
|
227
|
+
assert_equal [false, true], session.document.root.children.map { |n| n.opts[:scroll] }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def test_column_and_row_are_containers_carrying_gap_align_pad_in_opts
|
|
231
|
+
session = build_session
|
|
232
|
+
|
|
233
|
+
session.column(:c, gap: 8, align: :stretch, pad: 4) { |c| c.button(:go) }
|
|
234
|
+
|
|
235
|
+
node = session.document.root.children.first
|
|
236
|
+
assert_equal :column, node.type
|
|
237
|
+
assert_equal({ gap: 8, align: :stretch, pad: 4 }, node.opts)
|
|
238
|
+
assert_equal [:button], node.children.map(&:type)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_row_defaults_gap_align_pad_when_not_given
|
|
242
|
+
session = build_session
|
|
243
|
+
|
|
244
|
+
session.row(:r)
|
|
245
|
+
|
|
246
|
+
node = session.document.root.children.first
|
|
247
|
+
assert_equal :row, node.type
|
|
248
|
+
assert_equal({}, node.opts)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def test_grow_is_captured_on_the_childs_layout_and_stripped_from_opts
|
|
252
|
+
session = build_session
|
|
253
|
+
|
|
254
|
+
session.column(:c) { |c| c.button(:go, text: 'Go', grow: true) }
|
|
255
|
+
|
|
256
|
+
button_node = session.document.root.children.first.children.first
|
|
257
|
+
assert_equal({ grow: true }, button_node.layout)
|
|
258
|
+
assert_equal({ text: 'Go' }, button_node.opts)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def test_grow_defaults_to_nil_layout_when_not_given
|
|
262
|
+
session = build_session
|
|
263
|
+
|
|
264
|
+
session.button(:go)
|
|
265
|
+
|
|
266
|
+
node = session.document.root.children.first
|
|
267
|
+
assert_nil node.layout
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def test_grow_works_on_a_container_child_too
|
|
271
|
+
session = build_session
|
|
272
|
+
|
|
273
|
+
session.column(:outer) { |o| o.row(:inner, grow: true) }
|
|
274
|
+
|
|
275
|
+
inner_node = session.document.root.children.first.children.first
|
|
276
|
+
assert_equal({ grow: true }, inner_node.layout)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def test_lazy_true_marks_the_container_node_lazy_and_is_stripped_from_opts
|
|
280
|
+
session = build_session
|
|
281
|
+
|
|
282
|
+
session.panel(:picker, lazy: true, text: 'ignored opt just for this test')
|
|
283
|
+
|
|
284
|
+
node = session.document.root.children.first
|
|
285
|
+
assert node.lazy?
|
|
286
|
+
refute node.opts.key?(:lazy)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_lazy_defaults_to_false_when_not_given
|
|
290
|
+
session = build_session
|
|
291
|
+
|
|
292
|
+
session.panel(:picker)
|
|
293
|
+
|
|
294
|
+
node = session.document.root.children.first
|
|
295
|
+
refute node.lazy?
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_spacer_is_a_leaf_node_with_grow_baked_in
|
|
299
|
+
session = build_session
|
|
300
|
+
|
|
301
|
+
session.column(:c) { |c| c.spacer }
|
|
302
|
+
|
|
303
|
+
spacer_node = session.document.root.children.first.children.first
|
|
304
|
+
assert_equal :spacer, spacer_node.type
|
|
305
|
+
assert_equal({ grow: true }, spacer_node.layout)
|
|
306
|
+
assert_equal [], spacer_node.children
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def test_grid_is_a_container_type
|
|
310
|
+
session = build_session
|
|
311
|
+
|
|
312
|
+
session.grid(:g, gap: 6) { |g| g.cell(row: 0, col: 0) { g.label(text: 'User') } }
|
|
313
|
+
|
|
314
|
+
node = session.document.root.children.first
|
|
315
|
+
assert_equal :grid, node.type
|
|
316
|
+
assert_equal({ gap: 6 }, node.opts)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def test_cell_tags_the_single_widget_it_creates_with_row_col_span
|
|
320
|
+
session = build_session
|
|
321
|
+
|
|
322
|
+
session.grid(:g) { |g| g.cell(row: 1, col: 2, span: 3) { g.label(:l, text: 'x') } }
|
|
323
|
+
|
|
324
|
+
label_node = session.document.root.children.first.children.first
|
|
325
|
+
assert_equal({ row: 1, col: 2, span: 3 }, label_node.layout[:cell])
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def test_cell_span_defaults_to_1
|
|
329
|
+
session = build_session
|
|
330
|
+
|
|
331
|
+
session.grid(:g) { |g| g.cell(row: 0, col: 0) { g.label(:l, text: 'x') } }
|
|
332
|
+
|
|
333
|
+
label_node = session.document.root.children.first.children.first
|
|
334
|
+
assert_equal 1, label_node.layout[:cell][:span]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def test_cell_merges_with_an_existing_grow_layout_intent
|
|
338
|
+
session = build_session
|
|
339
|
+
|
|
340
|
+
session.grid(:g) { |g| g.cell(row: 0, col: 0) { g.text_box(:t, grow: true) } }
|
|
341
|
+
|
|
342
|
+
node = session.document.root.children.first.children.first
|
|
343
|
+
assert_equal true, node.layout[:grow]
|
|
344
|
+
assert_equal({ row: 0, col: 0, span: 1 }, node.layout[:cell])
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def test_cell_raises_if_its_block_creates_no_widget
|
|
348
|
+
session = build_session
|
|
349
|
+
|
|
350
|
+
error = assert_raises(ArgumentError) { session.grid(:g) { |g| g.cell(row: 0, col: 0) { } } }
|
|
351
|
+
assert_match(/exactly one widget/, error.message)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def test_cell_raises_if_its_block_creates_more_than_one_widget
|
|
355
|
+
session = build_session
|
|
356
|
+
|
|
357
|
+
error = assert_raises(ArgumentError) do
|
|
358
|
+
session.grid(:g) { |g| g.cell(row: 0, col: 0) { g.label(:a); g.label(:b) } }
|
|
359
|
+
end
|
|
360
|
+
assert_match(/exactly one widget/, error.message)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def test_cell_outside_a_grid_raises
|
|
364
|
+
session = build_session
|
|
365
|
+
|
|
366
|
+
error = assert_raises(ArgumentError) { session.cell(row: 0, col: 0) { } }
|
|
367
|
+
assert_match(/grid/, error.message)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def test_stretch_sets_stretch_columns_and_rows_on_the_grid_nodes_opts
|
|
371
|
+
session = build_session
|
|
372
|
+
|
|
373
|
+
session.grid(:g) { |g| g.stretch(columns: [1], rows: [0]) }
|
|
374
|
+
|
|
375
|
+
node = session.document.root.children.first
|
|
376
|
+
assert_equal [1], node.opts[:stretch_columns]
|
|
377
|
+
assert_equal [0], node.opts[:stretch_rows]
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def test_stretch_outside_a_grid_raises
|
|
381
|
+
session = build_session
|
|
382
|
+
|
|
383
|
+
error = assert_raises(ArgumentError) { session.stretch(columns: [0]) }
|
|
384
|
+
assert_match(/grid/, error.message)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def test_tabs_is_a_container_type
|
|
388
|
+
session = build_session
|
|
389
|
+
|
|
390
|
+
session.tabs(:t) { |t| t.tab('General') { |g| g.button(:go, text: 'Go') } }
|
|
391
|
+
|
|
392
|
+
node = session.document.root.children.first
|
|
393
|
+
assert_equal :tabs, node.type
|
|
394
|
+
assert_equal [:tab], node.children.map(&:type)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def test_tab_stashes_its_label_and_nests_its_blocks_children
|
|
398
|
+
session = build_session
|
|
399
|
+
|
|
400
|
+
session.tabs { |t| t.tab('General') { |g| g.button(:go, text: 'Go') } }
|
|
401
|
+
|
|
402
|
+
tab_node = session.document.root.children.first.children.first
|
|
403
|
+
assert_equal 'General', tab_node.opts[:tab_label]
|
|
404
|
+
assert_equal [:button], tab_node.children.map(&:type)
|
|
405
|
+
assert_equal [:go], tab_node.children.map(&:name)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def test_tab_accepts_an_optional_name_for_ui_bracket_lookup
|
|
409
|
+
session = build_session
|
|
410
|
+
|
|
411
|
+
session.tabs { |t| t.tab('Advanced', :advanced_tab) }
|
|
412
|
+
|
|
413
|
+
assert_equal :advanced_tab, session[:advanced_tab].name
|
|
414
|
+
assert_equal :tab, session[:advanced_tab].type
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def test_tab_outside_ui_tabs_raises
|
|
418
|
+
session = build_session
|
|
419
|
+
|
|
420
|
+
error = assert_raises(ArgumentError) { session.tab('General') }
|
|
421
|
+
assert_match(/ui\.tabs/, error.message)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def test_split_is_a_container_type
|
|
425
|
+
session = build_session
|
|
426
|
+
|
|
427
|
+
session.split { |s| s.pane { |p| p.button(:go, text: 'Go') } }
|
|
428
|
+
|
|
429
|
+
node = session.document.root.children.first
|
|
430
|
+
assert_equal :split, node.type
|
|
431
|
+
assert_equal [:pane], node.children.map(&:type)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_split_defaults_to_horizontal_orientation
|
|
435
|
+
session = build_session
|
|
436
|
+
|
|
437
|
+
session.split { }
|
|
438
|
+
|
|
439
|
+
node = session.document.root.children.first
|
|
440
|
+
assert_equal 'horizontal', node.opts[:orient]
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def test_split_accepts_vertical_orientation
|
|
444
|
+
session = build_session
|
|
445
|
+
|
|
446
|
+
session.split(orientation: :vertical) { }
|
|
447
|
+
|
|
448
|
+
node = session.document.root.children.first
|
|
449
|
+
assert_equal 'vertical', node.opts[:orient]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def test_split_raises_on_an_invalid_orientation
|
|
453
|
+
session = build_session
|
|
454
|
+
|
|
455
|
+
error = assert_raises(ArgumentError) { session.split(orientation: :diagonal) }
|
|
456
|
+
assert_match(/:horizontal or :vertical/, error.message)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def test_split_accepts_a_name_like_any_other_container
|
|
460
|
+
session = build_session
|
|
461
|
+
|
|
462
|
+
session.split(:main) { |s| s.pane { } }
|
|
463
|
+
|
|
464
|
+
node = session.document.root.children.first
|
|
465
|
+
assert_equal :main, node.name
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def test_pane_nests_its_blocks_children
|
|
469
|
+
session = build_session
|
|
470
|
+
|
|
471
|
+
session.split { |s| s.pane { |p| p.button(:go, text: 'Go') } }
|
|
472
|
+
|
|
473
|
+
pane_node = session.document.root.children.first.children.first
|
|
474
|
+
assert_equal [:button], pane_node.children.map(&:type)
|
|
475
|
+
assert_equal [:go], pane_node.children.map(&:name)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def test_pane_accepts_an_optional_name_for_ui_bracket_lookup
|
|
479
|
+
session = build_session
|
|
480
|
+
|
|
481
|
+
session.split { |s| s.pane(:left) }
|
|
482
|
+
|
|
483
|
+
assert_equal :left, session[:left].name
|
|
484
|
+
assert_equal :pane, session[:left].type
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def test_pane_weight_is_stashed_in_opts_without_leaking_weight_itself
|
|
488
|
+
session = build_session
|
|
489
|
+
|
|
490
|
+
session.split { |s| s.pane(weight: 2) }
|
|
491
|
+
|
|
492
|
+
pane_node = session.document.root.children.first.children.first
|
|
493
|
+
assert_equal 2, pane_node.opts[:pane_weight]
|
|
494
|
+
refute pane_node.opts.key?(:weight)
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def test_pane_without_a_weight_stashes_nothing
|
|
498
|
+
session = build_session
|
|
499
|
+
|
|
500
|
+
session.split { |s| s.pane }
|
|
501
|
+
|
|
502
|
+
pane_node = session.document.root.children.first.children.first
|
|
503
|
+
refute pane_node.opts.key?(:pane_weight)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def test_pane_outside_ui_split_raises
|
|
507
|
+
session = build_session
|
|
508
|
+
|
|
509
|
+
error = assert_raises(ArgumentError) { session.pane }
|
|
510
|
+
assert_match(/ui\.split/, error.message)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def test_raw_creates_a_raw_op_node_attached_to_the_current_parent
|
|
514
|
+
session = build_session
|
|
515
|
+
|
|
516
|
+
session.column(:c) { |c| c.raw { |_app| } }
|
|
517
|
+
|
|
518
|
+
node = session.document.root.children.first.children.first
|
|
519
|
+
assert_equal :raw_op, node.type
|
|
520
|
+
assert_kind_of Proc, node.opts[:block]
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def test_raw_does_not_execute_the_block_during_build
|
|
524
|
+
session = build_session
|
|
525
|
+
executed = false
|
|
526
|
+
|
|
527
|
+
session.raw { |_app| executed = true }
|
|
528
|
+
|
|
529
|
+
refute executed, "ui.raw should defer its block to realize, not run it during build"
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def test_scrollable_nests_children_declared_in_its_block
|
|
533
|
+
session = build_session
|
|
534
|
+
|
|
535
|
+
session.scrollable(:region) do |s|
|
|
536
|
+
s.list(:items)
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
node = session.document.root.children.first
|
|
540
|
+
assert_equal :scrollable, node.type
|
|
541
|
+
assert_equal [:list], node.children.map(&:type)
|
|
542
|
+
assert_equal [:items], node.children.map(&:name)
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def test_scrollable_x_and_y_opts_land_on_the_node
|
|
546
|
+
session = build_session
|
|
547
|
+
|
|
548
|
+
session.scrollable(:region, x: true, y: false) { |s| s.list(:items) }
|
|
549
|
+
|
|
550
|
+
node = session.document.root.children.first
|
|
551
|
+
assert_equal true, node.opts[:x]
|
|
552
|
+
assert_equal false, node.opts[:y]
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def test_scrollable_without_a_block_still_creates_a_childless_node
|
|
556
|
+
session = build_session
|
|
557
|
+
|
|
558
|
+
session.scrollable(:region)
|
|
559
|
+
|
|
560
|
+
node = session.document.root.children.first
|
|
561
|
+
assert_equal :scrollable, node.type
|
|
562
|
+
assert_equal [], node.children
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def test_screens_is_memoized_across_calls
|
|
566
|
+
session = build_session
|
|
567
|
+
|
|
568
|
+
assert_same session.screens, session.screens
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def test_modal_defaults_to_nil
|
|
572
|
+
session = build_session
|
|
573
|
+
|
|
574
|
+
assert_nil session.modal
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def test_modal_is_assignable_and_reads_back
|
|
578
|
+
session = build_session
|
|
579
|
+
stack = Teek::UI::ModalStack.new(on_enter: ->(_) { }, on_exit: -> { })
|
|
580
|
+
|
|
581
|
+
session.modal = stack
|
|
582
|
+
|
|
583
|
+
assert_same stack, session.modal
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def test_current_path_at_the_top_level_is_top_level
|
|
587
|
+
session = build_session
|
|
588
|
+
|
|
589
|
+
assert_equal '(top level)', session.current_path
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def test_current_path_reflects_open_containers
|
|
593
|
+
session = build_session
|
|
594
|
+
|
|
595
|
+
session.column do |c|
|
|
596
|
+
c.row { assert_equal 'column > row', session.current_path }
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def test_current_path_includes_a_containers_name
|
|
601
|
+
session = build_session
|
|
602
|
+
|
|
603
|
+
session.column(:ctrl) { assert_equal 'column(:ctrl)', session.current_path }
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def test_current_path_returns_to_top_level_after_the_block_closes
|
|
607
|
+
session = build_session
|
|
608
|
+
|
|
609
|
+
session.column { }
|
|
610
|
+
|
|
611
|
+
assert_equal '(top level)', session.current_path
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
def test_current_path_inside_a_menu_subtree
|
|
615
|
+
session = build_session
|
|
616
|
+
|
|
617
|
+
session.menu_bar do |mb|
|
|
618
|
+
mb.menu(label: 'File') { assert_equal 'menu_bar > menu', session.current_path }
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
end
|