terminal-layout 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,745 @@
1
+ require 'spec_helper'
2
+ require 'pry'
3
+
4
+ def print_tree(render_tree, indent=0)
5
+ render_tree.children.each do |box|
6
+ print " " * indent
7
+ puts box
8
+ print_tree box, indent + 2
9
+ end
10
+ end
11
+
12
+ def find_first_in_tree(tree, comparator_proc)
13
+ tree.children.each do |node|
14
+ return node if comparator_proc.call(node)
15
+ find_first_in_tree node, comparator_proc
16
+ end
17
+ nil
18
+ end
19
+
20
+ def find_all_in_tree(tree, comparator_proc)
21
+ return nil if tree.children.empty?
22
+ tree.children.inject([]) do |results, node|
23
+ if comparator_proc.call(node)
24
+ results.push node
25
+ else
26
+ results.push find_first_in_tree(node, comparator_proc)
27
+ end
28
+ results.compact
29
+ end
30
+ end
31
+
32
+
33
+ module TerminalLayout
34
+
35
+ describe "Laying things out" do
36
+ subject(:render_tree){ RenderTree.new(view, parent:nil, style:style).tap{ |rt| rt.layout } }
37
+ let(:view){ Box.new(style: style, children: children) }
38
+ let(:style){ raise(NotImplementedError, "Must provide :children") }
39
+ let(:children){ raise(NotImplementedError, "Must provide :children") }
40
+
41
+ def first_rendered(box, parent: render_tree)
42
+ find_first_in_tree(parent, ->(node){ node.box == box })
43
+ end
44
+
45
+ def all_rendered(box, parent: render_tree)
46
+ find_all_in_tree(parent, ->(node){ node.box == box })
47
+ end
48
+
49
+ after(:each) do |example|
50
+ if example.exception
51
+ puts
52
+ puts " #{example.location} #{example.description}"
53
+ print_tree render_tree
54
+ end
55
+ end
56
+
57
+ describe "normal block flow - vertical stacking" do
58
+ context "with one block child" do
59
+ let(:style){ {width:10, height: 10} }
60
+ let(:children){ [block_a] }
61
+ let(:block_a){ Box.new(style: {display: :block, height: 1}) }
62
+ let(:rendered_element){ first_rendered(block_a) }
63
+
64
+ it "positions the box at (0,0) with the same width as its containing box" do
65
+ expect(rendered_element.position).to eq(Position.new(0, 0))
66
+ expect(rendered_element.size).to eq(Dimension.new(10, 1))
67
+ end
68
+ end
69
+
70
+ context "block with content" do
71
+ let(:style){ {width:10, height: 10} }
72
+ let(:children){ [block_a] }
73
+ let(:block_a){ Box.new(content: "Foobar", style: {display: :block}) }
74
+ let(:rendered_element){ first_rendered(block_a) }
75
+
76
+ it "sets the height" do
77
+ expect(rendered_element.position).to eq(Position.new(0, 0))
78
+ expect(rendered_element.size).to eq(Dimension.new(10, 1))
79
+ end
80
+ end
81
+
82
+ context "block with height set" do
83
+ let(:style){ {width:10, height: 10} }
84
+ let(:children){ [block_a] }
85
+ let(:block_a){ Box.new(style: {display: :block, height:4}, children:[
86
+ Box.new(content: "Foobar", style: {display: :inline})
87
+ ])}
88
+ let(:rendered_element){ first_rendered(block_a) }
89
+
90
+ it "doesn't shrink the height when the content needs less" do
91
+ expect(block_a.height).to eq 4
92
+ end
93
+
94
+ it "doesn't grow the height when the content needs more" do
95
+ block_a.content = "Foobar" * 100
96
+ expect(block_a.height).to eq 4
97
+ end
98
+
99
+ it "doesn't affect the height of the render tree" do
100
+ expect(render_tree.height).to eq 10
101
+ end
102
+ end
103
+
104
+ context "with multiple block children" do
105
+ let(:style){ {width:10, height: 10} }
106
+ let(:children){ [block_a, block_b] }
107
+ let(:block_a){ Box.new(style: {display: :block, height: 1}) }
108
+ let(:block_b){ Box.new(style: {display: :block, height: 1}) }
109
+
110
+ let(:rendered_element_a){ first_rendered(block_a) }
111
+ let(:rendered_element_b){ first_rendered(block_b) }
112
+
113
+ it "stacks the boxes vertically with the widths matching the containing box" do
114
+ expect(render_tree.children.length).to eq(2)
115
+
116
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
117
+ expect(rendered_element_a.size).to eq(Dimension.new(10, 1))
118
+
119
+ expect(rendered_element_b.position).to eq(Position.new(0, 1))
120
+ expect(rendered_element_b.size).to eq(Dimension.new(10, 1))
121
+ end
122
+ end
123
+
124
+ context "block content following inline content" do
125
+ let(:style){ {width:10, height: 10} }
126
+ let(:children){ [inline_a, block_b] }
127
+ let(:inline_a){ Box.new(content: "ABCDE", style: {display: :inline}) }
128
+ let(:block_b){ Box.new(style: {display: :block, height: 1, width: 5}) }
129
+
130
+ let(:rendered_element_a){ first_rendered(inline_a) }
131
+ let(:rendered_element_b){ first_rendered(block_b) }
132
+
133
+ it "puts the block on the next line even though they would both fit on a single line" do
134
+ expect(render_tree.children.length).to eq(2)
135
+
136
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
137
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
138
+
139
+ expect(rendered_element_b.position).to eq(Position.new(0, 1))
140
+ expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
141
+ end
142
+ end
143
+
144
+ context "with a block child that has 0 height" do
145
+ let(:style){ {width:10, height: 10} }
146
+ let(:children){ [block_0_height] }
147
+ let(:block_0_height){ Box.new(style: {display: :block, height: 0}) }
148
+
149
+ it "doesn't include it in the layout render_tree" do
150
+ expect(render_tree.children.length).to eq(0)
151
+ end
152
+ end
153
+
154
+ context "with a block child that has 0 width" do
155
+ let(:style){ {width:10, height: 10} }
156
+ let(:children){ [block_0_width] }
157
+ let(:block_0_width){ Box.new(style: {display: :block, width: 0}) }
158
+
159
+ it "doesn't include it in the layout render_tree" do
160
+ expect(render_tree.children.length).to eq(0)
161
+ end
162
+ end
163
+ end
164
+
165
+ describe "normal flow - inline elements" do
166
+ context "with multiple inline children that fit within the width of the parent" do
167
+ let(:style){ {width:10, height: 10} }
168
+ let(:children){ [inline_a, inline_b] }
169
+ let(:inline_a){ Box.new(content:"ABC", style: {display: :inline}) }
170
+ let(:inline_b){ Box.new(content:"DEFGHIJ", style: {display: :inline}) }
171
+
172
+ let(:rendered_element_a){ first_rendered(inline_a) }
173
+ let(:rendered_element_b){ first_rendered(inline_b) }
174
+
175
+ it "horizontally lays out the two boxes with the width matching their respective content's length" do
176
+ expect(render_tree.children.length).to eq(2)
177
+
178
+ expect(rendered_element_a.content).to eq("ABC")
179
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
180
+ expect(rendered_element_a.size).to eq(Dimension.new(3, 1))
181
+
182
+ expect(rendered_element_b.content).to eq("DEFGHIJ")
183
+ expect(rendered_element_b.position).to eq(Position.new(3, 0))
184
+ expect(rendered_element_b.size).to eq(Dimension.new(7, 1))
185
+ end
186
+ end
187
+
188
+ context "with multiple inline children that do not fit within the width of the parent" do
189
+ let(:style){ {width:10, height: 10} }
190
+ let(:children){ [inline_a, inline_b] }
191
+ let(:inline_a){ Box.new(content:"ABCDEFG", style: {display: :inline}) }
192
+ let(:inline_b){ Box.new(content:"HIJKLMNO", style: {display: :inline}) }
193
+
194
+ let(:rendered_element_a){ first_rendered(inline_a) }
195
+ let(:rendered_elements_b){ all_rendered(inline_b) }
196
+
197
+ it "horizontally lays out the two boxes wrapping lines as needed" do
198
+ expect(render_tree.children.length).to eq(3)
199
+
200
+ # row 1
201
+ expect(rendered_element_a.content).to eq("ABCDEFG")
202
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
203
+ expect(rendered_element_a.size).to eq(Dimension.new(7, 1))
204
+
205
+ expect(rendered_elements_b[0].content).to eq("HIJ")
206
+ expect(rendered_elements_b[0].position).to eq(Position.new(7, 0))
207
+ expect(rendered_elements_b[0].size).to eq(Dimension.new(3, 1))
208
+
209
+ # row 2
210
+ expect(rendered_elements_b[1].content).to eq("KLMNO")
211
+ expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
212
+ expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
213
+ end
214
+ end
215
+ end
216
+
217
+ describe "normal flow - float left" do
218
+ context "with a single element float left without a width" do
219
+ let(:style){ {width:10, height: 10} }
220
+ let(:children){ [float_a] }
221
+ let(:float_a){ Box.new(style: {display: :float, float: :left}) }
222
+
223
+ it "doesn't include the element in the render render_tree" do
224
+ expect(render_tree.children.length).to eq(0)
225
+ end
226
+ end
227
+
228
+ context "with a single element float left without a height" do
229
+ let(:style){ {width:10, height: 10} }
230
+ let(:children){ [float_a] }
231
+ let(:float_a){ Box.new(style: {display: :float, float: :left, width: 1}) }
232
+
233
+ it "doesn't include the element in the render render_tree" do
234
+ expect(render_tree.children.length).to eq(0)
235
+ end
236
+ end
237
+
238
+ context "a float left element without a height that has a block child" do
239
+ let(:style){ {width:10, height: 10} }
240
+ let(:children){ [float_a] }
241
+ let(:float_a){ Box.new(style: {display: :float, float: :left, width: 1}, children: [block_b]) }
242
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
243
+
244
+ let(:rendered_element_a){ first_rendered(float_a) }
245
+
246
+ it "gets its height from its children" do
247
+ expect(rendered_element_a.height).to eq(1)
248
+ end
249
+ end
250
+
251
+ context "a float left element without a height that has multiple block children" do
252
+ let(:style){ {width:10, height: 10} }
253
+ let(:children){ [float_a] }
254
+ let(:float_a){ Box.new(style: {display: :float, float: :left, width: 10}, children: [block_b, block_c]) }
255
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
256
+ let(:block_c){ Box.new(style: {width: 5, height: 4, display: :block}) }
257
+
258
+ let(:rendered_element_a){ first_rendered(float_a) }
259
+
260
+ it "gets its height from its children" do
261
+ expect(rendered_element_a.height).to eq(5)
262
+ end
263
+ end
264
+
265
+ context "a float left element without a height that has inline children" do
266
+ let(:style){ {width:10, height: 10} }
267
+ let(:children){ [float_a] }
268
+ let(:float_a){ Box.new(style: {display: :float, float: :left, width: 5}, children: [inline_b]) }
269
+ let(:inline_b){ Box.new(content: "ABCDEFGHIJK", style: {display: :inline}) }
270
+
271
+ let(:rendered_element_a){ first_rendered(float_a) }
272
+
273
+ it "gets its height from its children" do
274
+ # Line 1: ABCDE, Line 2: FGHIJ, Line 3: K
275
+ expect(rendered_element_a.height).to eq(3)
276
+ end
277
+ end
278
+
279
+ context "with a single element float left with a width" do
280
+ let(:style){ {width:10, height: 10} }
281
+ let(:children){ [float_a] }
282
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
283
+
284
+ let(:rendered_element_a){ first_rendered(float_a) }
285
+
286
+ it "includes the element at the left-most position for the current row" do
287
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
288
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
289
+ end
290
+ end
291
+
292
+ context "with a float left followed by a block element" do
293
+ let(:style){ {width:10, height: 10} }
294
+ let(:children){ [float_a, block_b] }
295
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
296
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
297
+
298
+ let(:rendered_element_a){ first_rendered(float_a) }
299
+ let(:rendered_element_b){ first_rendered(block_b) }
300
+
301
+ context "and they both fit on the same line" do
302
+ it "puts the float left element before the block element" do
303
+ # float
304
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
305
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
306
+
307
+ # block
308
+ expect(rendered_element_b.position).to eq(Position.new(5, 0))
309
+ expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
310
+ end
311
+ end
312
+
313
+ context "and they do not fit on the same line" do
314
+ before do
315
+ block_b.width = 6
316
+ end
317
+
318
+ it "puts the block element on the next line when they both dont fit" do
319
+ # float
320
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
321
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
322
+
323
+ # block
324
+ expect(rendered_element_b.position).to eq(Position.new(0, 1))
325
+ expect(rendered_element_b.size).to eq(Dimension.new(6, 1))
326
+ end
327
+ end
328
+ end
329
+
330
+ context "with a float left followed by an block" do
331
+ let(:style){ {width:10, height: 10} }
332
+ let(:children){ [float_a, inline_b] }
333
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
334
+ let(:inline_b){ Box.new(content: "ABCD", style: {height: 1, display: :inline}) }
335
+
336
+ let(:rendered_element_a){ first_rendered(float_a) }
337
+ let(:rendered_element_b){ first_rendered(inline_b) }
338
+
339
+ context "and they both fit on the same line" do
340
+ it "puts it before inline elements on the same line" do
341
+ # float
342
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
343
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
344
+
345
+ # inline
346
+ expect(rendered_element_b.position).to eq(Position.new(5, 0))
347
+ expect(rendered_element_b.size).to eq(Dimension.new(4, 1))
348
+ end
349
+ end
350
+
351
+ context "and they do not fit on the same line" do
352
+ before do
353
+ inline_b.content = "ABCDEFGHIJ"
354
+ end
355
+
356
+ let(:rendered_elements_b){ all_rendered(inline_b) }
357
+
358
+ it "wraps the text onto the next line" do
359
+ # float
360
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
361
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
362
+
363
+ # inline
364
+ expect(rendered_elements_b[0].position).to eq(Position.new(5, 0))
365
+ expect(rendered_elements_b[0].size).to eq(Dimension.new(5, 1))
366
+
367
+ expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
368
+ expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
369
+ end
370
+ end
371
+ end
372
+
373
+ context "with multiple elements floated left" do
374
+ let(:style){ {width:10, height: 10} }
375
+ let(:children){ [float_a, float_b, float_c, float_d] }
376
+ let(:float_a){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
377
+ let(:float_b){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
378
+ let(:float_c){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
379
+ let(:float_d){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
380
+
381
+ let(:rendered_element_a){ first_rendered(float_a) }
382
+ let(:rendered_element_b){ first_rendered(float_b) }
383
+ let(:rendered_element_c){ first_rendered(float_c) }
384
+ let(:rendered_element_d){ first_rendered(float_d) }
385
+
386
+ it "places them horizontally next to each other" do
387
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
388
+ expect(rendered_element_b.position).to eq(Position.new(3, 0))
389
+ expect(rendered_element_c.position).to eq(Position.new(6, 0))
390
+ end
391
+
392
+ it "wraps floats that don't fit on the current line to the next line" do
393
+ expect(rendered_element_d.position).to eq(Position.new(0, 1))
394
+ end
395
+ end
396
+
397
+ context "with a float left element that is taller than a single line" do
398
+ let(:style){ {width:10, height: 10} }
399
+ let(:children){ [float_a, block_b, block_c] }
400
+ let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}) }
401
+ let(:block_b){ Box.new(style: {width: 3, height: 2, display: :block}) }
402
+ let(:block_c){ Box.new(style: {width: 3, height: 2, display: :block}) }
403
+
404
+ let(:rendered_element_a){ first_rendered(float_a) }
405
+ let(:rendered_element_b){ first_rendered(block_b) }
406
+ let(:rendered_element_c){ first_rendered(block_c) }
407
+
408
+ it "spans multiple lines" do
409
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
410
+ expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
411
+ end
412
+
413
+ it "positions block elements next to the float on all lines" do
414
+ expect(rendered_element_b.position).to eq(Position.new(3, 0))
415
+ expect(rendered_element_b.size).to eq(Dimension.new(3, 2))
416
+
417
+ expect(rendered_element_c.position).to eq(Position.new(3, 2))
418
+ expect(rendered_element_c.size).to eq(Dimension.new(3, 2))
419
+ end
420
+ end
421
+
422
+ context "nested floats" do
423
+ let(:style){ {width:10, height: 10} }
424
+ let(:children){ [float_a] }
425
+ let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
426
+ float_b
427
+ ])}
428
+ let(:float_b){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
429
+ float_c
430
+ ])}
431
+ let(:float_c){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
432
+ float_d,
433
+ block_e
434
+ ])}
435
+ let(:float_d){ Box.new(style: {width: 2, height: 5, display: :float, float: :left}) }
436
+ let(:block_e){ Box.new(style: {width: 1, height: 5, display: :block}) }
437
+
438
+ let(:rendered_element_a){ first_rendered(float_a) }
439
+ let(:rendered_element_b){ first_rendered(float_b, parent: rendered_element_a) }
440
+ let(:rendered_element_c){ first_rendered(float_c, parent: rendered_element_b) }
441
+ let(:rendered_element_d){ first_rendered(float_d, parent: rendered_element_c) }
442
+ let(:rendered_element_e){ first_rendered(block_e, parent: rendered_element_c) }
443
+
444
+ it "nests properly by aligning left elements to the left-most position" do
445
+ expect(rendered_element_a.position).to eq(Position.new(0, 0))
446
+ expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
447
+
448
+ expect(rendered_element_b.position).to eq(Position.new(0, 0))
449
+ expect(rendered_element_b.size).to eq(Dimension.new(3, 5))
450
+
451
+ expect(rendered_element_c.position).to eq(Position.new(0, 0))
452
+ expect(rendered_element_c.size).to eq(Dimension.new(3, 5))
453
+ end
454
+
455
+ it "still floats and positions elements correctly when nested" do
456
+ # float
457
+ expect(rendered_element_d.position).to eq(Position.new(0, 0))
458
+ expect(rendered_element_d.size).to eq(Dimension.new(2, 5))
459
+
460
+ # block gets placed beside the float
461
+ expect(rendered_element_e.position).to eq(Position.new(2, 0))
462
+ expect(rendered_element_e.size).to eq(Dimension.new(1, 5))
463
+ end
464
+ end
465
+ end
466
+
467
+ describe "normal flow - float right" do
468
+ context "with a single element float right without a width" do
469
+ let(:style){ {width:10, height: 10} }
470
+ let(:children){ [float_a] }
471
+ let(:float_a){ Box.new(style: {display: :float, float: :right}) }
472
+
473
+ it "doesn't include the element in the render render_tree" do
474
+ expect(render_tree.children.length).to eq(0)
475
+ end
476
+ end
477
+
478
+ context "with a single element float right without a height" do
479
+ let(:style){ {width:10, height: 10} }
480
+ let(:children){ [float_a] }
481
+ let(:float_a){ Box.new(style: {display: :float, float: :right, width: 1}) }
482
+
483
+ it "doesn't include the element in the render render_tree" do
484
+ expect(render_tree.children.length).to eq(0)
485
+ end
486
+ end
487
+
488
+ context "with a single element float right with a width" do
489
+ let(:style){ {width:10, height: 10} }
490
+ let(:children){ [float_a] }
491
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
492
+
493
+ let(:rendered_element_a){ first_rendered(float_a) }
494
+
495
+ it "includes the element at the right-most position minus its width for the current row" do
496
+ expect(rendered_element_a.position).to eq(Position.new(5, 0))
497
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
498
+ end
499
+ end
500
+
501
+ context "a float right element without a height that has a block child" do
502
+ let(:style){ {width:10, height: 10} }
503
+ let(:children){ [float_a] }
504
+ let(:float_a){ Box.new(style: {display: :float, float: :right, width: 1}, children: [block_b]) }
505
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
506
+
507
+ let(:rendered_element_a){ first_rendered(float_a) }
508
+
509
+ it "gets its height from its children" do
510
+ expect(rendered_element_a.height).to eq(1)
511
+ end
512
+ end
513
+
514
+ context "a float right element without a height that has multiple block children" do
515
+ let(:style){ {width:10, height: 10} }
516
+ let(:children){ [float_a] }
517
+ let(:float_a){ Box.new(style: {display: :float, float: :right, width: 10}, children: [block_b, block_c]) }
518
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
519
+ let(:block_c){ Box.new(style: {width: 5, height: 4, display: :block}) }
520
+
521
+ let(:rendered_element_a){ first_rendered(float_a) }
522
+
523
+ it "gets its height from its children" do
524
+ expect(rendered_element_a.height).to eq(5)
525
+ end
526
+ end
527
+
528
+ context "a float right element without a height that has inline children" do
529
+ let(:style){ {width:10, height: 10} }
530
+ let(:children){ [float_a] }
531
+ let(:float_a){ Box.new(style: {display: :float, float: :right, width: 5}, children: [inline_b]) }
532
+ let(:inline_b){ Box.new(content: "ABCDEFGHIJK", style: {display: :inline}) }
533
+
534
+ let(:rendered_element_a){ first_rendered(float_a) }
535
+
536
+ it "gets its height from its children" do
537
+ # Line 1: ABCDE, Line 2: FGHIJ, Line 3: K
538
+ expect(rendered_element_a.height).to eq(3)
539
+ end
540
+ end
541
+
542
+ context "with a float right followed by a block element" do
543
+ let(:style){ {width:10, height: 10} }
544
+ let(:children){ [float_a, block_b] }
545
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
546
+ let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
547
+
548
+ let(:rendered_element_a){ first_rendered(float_a) }
549
+ let(:rendered_element_b){ first_rendered(block_b) }
550
+
551
+ context "and they both fit on the same line" do
552
+ it "puts the block element before the float right element" do
553
+ # float
554
+ expect(rendered_element_a.position).to eq(Position.new(5, 0))
555
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
556
+
557
+ # block
558
+ expect(rendered_element_b.position).to eq(Position.new(0, 0))
559
+ expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
560
+ end
561
+ end
562
+
563
+ context "and they do not fit on the same line" do
564
+ before do
565
+ block_b.width = 6
566
+ end
567
+
568
+ it "puts the block element on the next line" do
569
+ # float
570
+ expect(rendered_element_a.position).to eq(Position.new(5, 0))
571
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
572
+
573
+ # block
574
+ expect(rendered_element_b.position).to eq(Position.new(0, 1))
575
+ expect(rendered_element_b.size).to eq(Dimension.new(6, 1))
576
+ end
577
+ end
578
+ end
579
+
580
+ context "with a float right followed by an inline element" do
581
+ let(:style){ {width:10, height: 10} }
582
+ let(:children){ [float_a, inline_b] }
583
+ let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
584
+ let(:inline_b){ Box.new(content: "ABCD", style: {height: 1, display: :inline}) }
585
+
586
+ let(:rendered_element_a){ first_rendered(float_a) }
587
+ let(:rendered_element_b){ first_rendered(inline_b) }
588
+
589
+ context "and they both fit on the same line" do
590
+ it "puts it after the inline elements on the same line" do
591
+ # float
592
+ expect(rendered_element_a.position).to eq(Position.new(5, 0))
593
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
594
+
595
+ # inline
596
+ expect(rendered_element_b.position).to eq(Position.new(0, 0))
597
+ expect(rendered_element_b.size).to eq(Dimension.new(4, 1))
598
+ end
599
+ end
600
+
601
+ context "and they do not fit on the same line" do
602
+ before do
603
+ inline_b.content = "ABCDEFGHIJ"
604
+ end
605
+
606
+ let(:rendered_elements_b){ all_rendered(inline_b) }
607
+
608
+ it "puts wraps the text onto the next line" do
609
+ # float
610
+ expect(rendered_element_a.position).to eq(Position.new(5, 0))
611
+ expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
612
+
613
+ # inline
614
+ expect(rendered_elements_b[0].position).to eq(Position.new(0, 0))
615
+ expect(rendered_elements_b[0].size).to eq(Dimension.new(5, 1))
616
+
617
+ expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
618
+ expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
619
+ end
620
+ end
621
+ end
622
+
623
+ context "with multiple elements floated left" do
624
+ let(:style){ {width:10, height: 10} }
625
+ let(:children){ [float_a, float_b, float_c, float_d] }
626
+ let(:float_a){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
627
+ let(:float_b){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
628
+ let(:float_c){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
629
+ let(:float_d){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
630
+
631
+ let(:rendered_element_a){ first_rendered(float_a) }
632
+ let(:rendered_element_b){ first_rendered(float_b) }
633
+ let(:rendered_element_c){ first_rendered(float_c) }
634
+ let(:rendered_element_d){ first_rendered(float_d) }
635
+
636
+ it "places them horizontally next to each other" do
637
+ expect(rendered_element_a.position).to eq(Position.new(7, 0))
638
+ expect(rendered_element_b.position).to eq(Position.new(4, 0))
639
+ expect(rendered_element_c.position).to eq(Position.new(1, 0))
640
+ end
641
+
642
+ it "wraps floats that don't fit on the current line to the next line" do
643
+ expect(rendered_element_d.position).to eq(Position.new(7, 1))
644
+ end
645
+ end
646
+
647
+ context "with a float right element that is taller than a single line" do
648
+ let(:style){ {width:10, height: 10} }
649
+ let(:children){ [float_a, block_b, block_c] }
650
+ let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :right}) }
651
+ let(:block_b){ Box.new(style: {width: 3, height: 2, display: :block}) }
652
+ let(:block_c){ Box.new(style: {width: 3, height: 2, display: :block}) }
653
+
654
+ let(:rendered_element_a){ first_rendered(float_a) }
655
+ let(:rendered_element_b){ first_rendered(block_b) }
656
+ let(:rendered_element_c){ first_rendered(block_c) }
657
+
658
+ it "spans multiple lines" do
659
+ expect(rendered_element_a.position).to eq(Position.new(7, 0))
660
+ expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
661
+ end
662
+
663
+ it "positions block elements next to the float on all lines" do
664
+ expect(rendered_element_b.position).to eq(Position.new(0, 0))
665
+ expect(rendered_element_b.size).to eq(Dimension.new(3, 2))
666
+
667
+ expect(rendered_element_c.position).to eq(Position.new(0, 2))
668
+ expect(rendered_element_c.size).to eq(Dimension.new(3, 2))
669
+ end
670
+ end
671
+
672
+ context "nested floats" do
673
+ let(:style){ {width:10, height: 10} }
674
+ let(:children){ [float_a] }
675
+ let(:float_a){ Box.new(style: {width: 7, height: 5, display: :float, float: :right}, children: [
676
+ float_b
677
+ ])}
678
+ let(:float_b){ Box.new(style: {width: 6, height: 5, display: :float, float: :right}, children: [
679
+ float_c
680
+ ])}
681
+ let(:float_c){ Box.new(style: {width: 5, height: 5, display: :float, float: :right}, children: [
682
+ float_d,
683
+ block_e
684
+ ])}
685
+ let(:float_d){ Box.new(style: {width: 2, height: 5, display: :float, float: :right}) }
686
+ let(:block_e){ Box.new(style: {width: 1, height: 5, display: :block}) }
687
+
688
+ let(:rendered_element_a){ first_rendered(float_a) }
689
+ let(:rendered_element_b){ first_rendered(float_b, parent: rendered_element_a) }
690
+ let(:rendered_element_c){ first_rendered(float_c, parent: rendered_element_b) }
691
+ let(:rendered_element_d){ first_rendered(float_d, parent: rendered_element_c) }
692
+ let(:rendered_element_e){ first_rendered(block_e, parent: rendered_element_c) }
693
+
694
+ it "nests properly by aligning right elements to the right-most position" do
695
+ expect(rendered_element_a.position).to eq(Position.new(3, 0))
696
+ expect(rendered_element_a.size).to eq(Dimension.new(7, 5))
697
+
698
+ expect(rendered_element_b.position).to eq(Position.new(1, 0))
699
+ expect(rendered_element_b.size).to eq(Dimension.new(6, 5))
700
+
701
+ expect(rendered_element_c.position).to eq(Position.new(1, 0))
702
+ expect(rendered_element_c.size).to eq(Dimension.new(5, 5))
703
+ end
704
+
705
+ it "still floats and positions elements correctly when nested" do
706
+ # float
707
+ expect(rendered_element_d.position).to eq(Position.new(3, 0))
708
+ expect(rendered_element_d.size).to eq(Dimension.new(2, 5))
709
+
710
+ # block gets placed beside the float
711
+ expect(rendered_element_e.position).to eq(Position.new(0, 0))
712
+ expect(rendered_element_e.size).to eq(Dimension.new(1, 5))
713
+ end
714
+ end
715
+ end
716
+
717
+ describe "offsets" do
718
+ let(:style){ {width:10, height: 10} }
719
+ let(:children){ [float_a, float_b, block_c, inline_d, inline_e, block_f] }
720
+ let(:float_a){ Box.new(content: "AAA", style: {width: 3, height: 1, display: :float, float: :left}) }
721
+ let(:float_b){ Box.new(content: "BBB", style: {width: 3, height: 1, display: :float, float: :right}) }
722
+ let(:block_c){ Box.new(content: "CCCCCCCCCCCC", style: {width: 6, height: 2, display: :block}) }
723
+ let(:inline_d){ Box.new(content: "ABCD", style: {display: :inline}) }
724
+ let(:inline_e){ Box.new(content: "DEFG", style: {display: :inline}) }
725
+ let(:block_f){ Box.new(content: "JJJJJJJJJJJJJJJJJJ", style: {width: 6, height: 3, display: :block}) }
726
+
727
+ let(:rendered_element_a){ first_rendered(float_a) }
728
+ let(:rendered_element_b){ first_rendered(float_b) }
729
+ let(:rendered_element_c){ first_rendered(block_c) }
730
+ let(:rendered_element_d){ first_rendered(inline_d) }
731
+ let(:rendered_element_e){ first_rendered(inline_e) }
732
+ let(:rendered_element_f){ first_rendered(block_f) }
733
+
734
+ it "knows its offset" do
735
+ expect(rendered_element_a.offset).to eq(Position.new(0, 0))
736
+ expect(rendered_element_b.offset).to eq(Position.new(7, 0))
737
+ expect(rendered_element_c.offset).to eq(Position.new(0, 1))
738
+ expect(rendered_element_d.offset).to eq(Position.new(0, 3))
739
+ expect(rendered_element_e.offset).to eq(Position.new(4, 3))
740
+ expect(rendered_element_f.offset).to eq(Position.new(0, 4))
741
+ end
742
+ end
743
+
744
+ end
745
+ end