uh-layout 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,170 @@
1
+ module Uh
2
+ class Layout
3
+ describe Container do
4
+ let(:entries) { %i[foo bar] }
5
+ subject(:container) { described_class.new entries }
6
+
7
+ describe '#initialize' do
8
+ it 'assigns no entries when no arguments are given' do
9
+ expect(described_class.new).to be_empty
10
+ end
11
+ end
12
+
13
+ describe '#to_ary' do
14
+ it 'supports implicit conversion to array' do
15
+ expect([] + container).to eq %i[foo bar]
16
+ end
17
+ end
18
+
19
+ describe '#current' do
20
+ context 'when container has multiple entries' do
21
+ it 'returns the first entry' do
22
+ expect(container.current).to be :foo
23
+ end
24
+ end
25
+
26
+ context 'when container has no entry' do
27
+ subject(:container) { described_class.new }
28
+
29
+ it 'returns nil' do
30
+ expect(container.current).to be nil
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#current=' do
36
+ context 'when given argument is an entry' do
37
+ before { container.current = :bar }
38
+
39
+ it 'assigns given entry as the current one' do
40
+ expect(container.current).to be :bar
41
+ end
42
+ end
43
+
44
+ context 'when given argument is not an entry' do
45
+ it 'does not change current entry' do
46
+ expect { container.current = :baz }
47
+ .not_to change { container.current }
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#<<' do
53
+ it 'adds given entry' do
54
+ container << :baz
55
+ expect(container).to include :baz
56
+ end
57
+ end
58
+
59
+ describe '#remove' do
60
+ let(:entries) { %i[foo bar baz] }
61
+
62
+ before { container.current = :bar }
63
+
64
+ it 'removes given argument from entries' do
65
+ container.remove :foo
66
+ expect(container).not_to include :foo
67
+ end
68
+
69
+ it 'preserves the current entry' do
70
+ container.remove :foo
71
+ expect(container.current).to be :bar
72
+ end
73
+
74
+ it 'returns self' do
75
+ expect(container.remove :foo).to be container
76
+ end
77
+
78
+ it 'raises an ArgumentError when given entry is not included' do
79
+ expect { container.remove :unknown_entry }.to raise_error ArgumentError
80
+ end
81
+
82
+ context 'when the first and current entry is removed' do
83
+ before do
84
+ container.current = :foo
85
+ container.remove :foo
86
+ end
87
+
88
+ it 'assigns next entry as the current one' do
89
+ expect(container.current).to be :bar
90
+ end
91
+ end
92
+
93
+ context 'when given entry is the only one' do
94
+ let(:entries) { [:foo] }
95
+
96
+ it 'has no more current entry' do
97
+ container.remove :foo
98
+ expect(container.current).to be nil
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#remove_if' do
104
+ it 'removes entries for which given block returns true' do
105
+ container.remove_if { |e| e == :foo }
106
+ expect(container).not_to include :foo
107
+ end
108
+ end
109
+
110
+ describe '#get' do
111
+ it 'returns consecutive entry in given direction' do
112
+ expect(container.get :succ).to be :bar
113
+ end
114
+
115
+ it 'returns nil when no consecutive entry exists' do
116
+ expect(container.get :pred).to be nil
117
+ end
118
+
119
+ context 'with cycle option' do
120
+ it 'returns consecutive entry, cycling before first one' do
121
+ expect(container.get :pred, cycle: true).to be :bar
122
+ end
123
+
124
+ it 'returns consecutive entry, cycling after last one' do
125
+ container.current = :bar
126
+ expect(container.get :succ, cycle: true).to be :foo
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#sel' do
132
+ it 'sets consecutive entry in given direction as the current one' do
133
+ container.sel :next
134
+ expect(container.current).to be :bar
135
+ end
136
+ end
137
+
138
+ describe '#set' do
139
+ let(:entries) { %i[foo bar baz] }
140
+
141
+ it 'swaps current entry with consecutive one in given direction' do
142
+ container.set :next
143
+ expect(container.to_a).to eq %i[bar foo baz]
144
+ end
145
+
146
+ it 'does not change current entry' do
147
+ expect { container.set :next }.not_to change { container.current }
148
+ end
149
+
150
+ context 'when direction is out of range' do
151
+ it 'rotates the entries' do
152
+ container.set :pred
153
+ expect(container.to_a).to eq %i[bar baz foo]
154
+ end
155
+
156
+ it 'does not change current entry' do
157
+ expect { container.set :pred }.not_to change { container.current }
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#swap' do
163
+ it 'swaps entries matched by given indexes' do
164
+ container.swap 0, 1
165
+ expect(container.to_a).to eq %i[bar foo]
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,40 @@
1
+ module Uh
2
+ class Layout
3
+ describe Screen do
4
+ let(:geo) { build_geo }
5
+ let(:other_geo) { build_geo 640, 0, 320, 240 }
6
+ let(:client) { build_client }
7
+ subject(:screen) { described_class.new(0, geo) }
8
+
9
+ it 'has one default tag with id 1 assigned' do
10
+ expect(screen.tags).to include an_object_having_attributes id: '1'
11
+ end
12
+
13
+ it 'has one default tag with screen geo copy assigned' do
14
+ expect(screen.tags.first.geo).to eq(screen.geo).and not_be screen.geo
15
+ end
16
+
17
+ describe '#height=' do
18
+ it 'changes screen height' do
19
+ expect { screen.height = 42 }.to change { screen.height }.to 42
20
+ end
21
+
22
+ it 'changes tags height' do
23
+ expect { screen.height = 42 }
24
+ .to change { screen.tags.first.height }.to 42
25
+ end
26
+ end
27
+
28
+ describe '#include?' do
29
+ it 'returns false when screen does not include given client' do
30
+ expect(screen.include? client).to be false
31
+ end
32
+
33
+ it 'returns true when screen includes given client' do
34
+ screen.current_tag.current_column_or_create << client
35
+ expect(screen.include? client).to be true
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ module Uh
2
+ class Layout
3
+ describe Tag do
4
+ let(:geo) { build_geo }
5
+ let(:other_geo) { build_geo 640, 0, 320, 240 }
6
+ let(:client) { build_client }
7
+ let(:other_client) { build_client }
8
+ let(:column) { Column.new(geo) }
9
+ subject(:tag) { described_class.new('1', geo) }
10
+
11
+ describe '.new' do
12
+ it 'raises error unless id converts to string' do
13
+ expect { described_class.new(1, geo) }.to raise_error(ArgumentError)
14
+ end
15
+ end
16
+
17
+ describe '#clients' do
18
+ it 'returns all clients contained in assigned columns' do
19
+ tag.columns << column.tap { |column| column << client << other_client }
20
+ expect(tag.clients).to eq [client, other_client]
21
+ end
22
+ end
23
+
24
+ describe '#include?' do
25
+ it 'returns false when tag does not include given client' do
26
+ expect(tag.include? client).to be false
27
+ end
28
+
29
+ it 'returns true when tag includes given client' do
30
+ tag.columns << column.tap { |column| column << client }
31
+ expect(tag.include? client).to be true
32
+ end
33
+ end
34
+
35
+ describe '#current_column_or_create' do
36
+ context 'when tag has no column' do
37
+ it 'creates a new column' do
38
+ expect { tag.current_column_or_create }
39
+ .to change { tag.columns.size }.from(0).to(1)
40
+ end
41
+
42
+ it 'returns the new column' do
43
+ expect(tag.current_column_or_create).to eq tag.columns.current
44
+ end
45
+ end
46
+
47
+ context 'when tag has a column' do
48
+ before { tag.columns << column }
49
+
50
+ it 'does not create any column' do
51
+ expect { tag.current_column_or_create }
52
+ .not_to change { tag.columns.size }
53
+ end
54
+
55
+ it 'returns the current column' do
56
+ expect(tag.current_column_or_create).to be column
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,414 @@
1
+ module Uh
2
+ describe Layout do
3
+ let(:geo) { build_geo }
4
+ let(:client) { build_client }
5
+ let(:other_client) { build_client }
6
+ let(:widget) { double('widget').as_null_object }
7
+ subject(:layout) { described_class.new }
8
+
9
+ before do
10
+ layout.screens << Layout::Screen.new(0, geo)
11
+ layout.screens << Layout::Screen.new(1, geo)
12
+ layout.widgets << widget
13
+ end
14
+
15
+ describe '#include?' do
16
+ it 'returns false when layout does not include given client' do
17
+ expect(layout.include? client).to be false
18
+ end
19
+
20
+ it 'returns true when layout includes given client' do
21
+ layout << client
22
+ expect(layout.include? client).to be true
23
+ end
24
+ end
25
+
26
+ describe '#arranger_for_current_tag' do
27
+ it 'returns an arranger for current tag columns and geo' do
28
+ expect(layout.arranger_for_current_tag)
29
+ .to respond_to(:update_geos)
30
+ .and have_attributes(
31
+ columns: layout.current_tag.columns,
32
+ geo: layout.current_tag.geo
33
+ )
34
+ end
35
+ end
36
+
37
+ describe '#update_widgets' do
38
+ it 'updates widgets' do
39
+ expect(layout.widgets).to all receive :update
40
+ layout.update_widgets
41
+ end
42
+
43
+ it 'redraws widgets' do
44
+ expect(layout.widgets).to all receive :redraw
45
+ layout.update_widgets
46
+ end
47
+ end
48
+
49
+ describe '#suggest_geo' do
50
+ it 'returns current tag geo copy' do
51
+ expect(layout.suggest_geo)
52
+ .to eq(layout.current_tag.geo)
53
+ .and not_be layout.current_tag.geo
54
+ end
55
+
56
+ context 'when current tag has a column' do
57
+ before { layout.current_tag.columns << Layout::Column.new(build_geo 42) }
58
+
59
+ it 'returns current column geo' do
60
+ expect(layout.suggest_geo)
61
+ .to eq(layout.current_column.geo)
62
+ .and not_be layout.current_column.geo
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#<<' do
68
+ before { layout << other_client }
69
+
70
+ it 'adds given client to current column' do
71
+ layout << client
72
+ expect(layout.current_column).to include client
73
+ end
74
+
75
+ it 'sets given client as the current one in current column' do
76
+ layout << client
77
+ expect(layout.current_column.current_client).to be client
78
+ end
79
+
80
+ it 'arranges current column clients' do
81
+ expect(layout.current_column).to receive :arrange_clients
82
+ layout << client
83
+ end
84
+
85
+ it 'shows and hides clients in current column' do
86
+ expect(layout.current_column).to receive :show_hide_clients
87
+ layout << client
88
+ end
89
+
90
+ it 'focuses given client' do
91
+ expect(client).to receive :focus
92
+ layout << client
93
+ end
94
+
95
+ it 'updates widgets' do
96
+ expect(widget).to receive :update
97
+ layout << client
98
+ end
99
+
100
+ it 'returns self' do
101
+ expect(layout << client).to be layout
102
+ end
103
+ end
104
+
105
+ describe '#remove' do
106
+ before { layout << client << other_client }
107
+
108
+ it 'removes given client from the layout' do
109
+ layout.remove client
110
+ expect(layout).not_to include client
111
+ end
112
+
113
+ it 'redraws columns with an arranger' do
114
+ expect_any_instance_of(Layout::Column::Arranger).to receive :redraw
115
+ layout.remove client
116
+ end
117
+
118
+ it 'arranges clients in removed client tag columns' do
119
+ expect(layout.current_tag.columns).to all receive :arrange_clients
120
+ layout.remove client
121
+ end
122
+
123
+ it 'shows and hides clients in removed client column' do
124
+ expect(layout.current_column).to receive :show_hide_clients
125
+ layout.remove client
126
+ end
127
+
128
+ it 'focuses the new current client' do
129
+ expect(other_client).to receive :focus
130
+ layout.remove client
131
+ end
132
+
133
+ it 'updates widgets' do
134
+ expect(widget).to receive :update
135
+ layout.remove client
136
+ end
137
+ end
138
+
139
+ describe 'handle_screen_sel' do
140
+ it 'selects consecutive screen in given direction' do
141
+ expect { layout.handle_screen_sel :succ }
142
+ .to change { layout.current_screen.id }.from(0).to(1)
143
+ end
144
+
145
+ it 'focus selected screen current client' do
146
+ layout << client
147
+ expect(client).to receive :focus
148
+ 2.times { layout.handle_screen_sel :succ }
149
+ end
150
+
151
+ it 'updates widgets' do
152
+ expect(widget).to receive :update
153
+ layout.handle_screen_sel :succ
154
+ end
155
+ end
156
+
157
+ describe 'handle_screen_set' do
158
+ before { layout << client }
159
+
160
+ it 'removes current client from origin screen' do
161
+ layout.handle_screen_set :succ
162
+ expect(layout.screens[0].tags.flat_map(&:clients)).not_to include client
163
+ end
164
+
165
+ it 'adds current client to consecutive screen in given direction' do
166
+ layout.handle_screen_set :succ
167
+ expect(layout.screens[1].tags.flat_map(&:clients)).to include client
168
+ end
169
+
170
+ it 'selects consecutive screen in given direction' do
171
+ expect { layout.handle_screen_set :succ }
172
+ .to change { layout.current_screen.id }.from(0).to(1)
173
+ end
174
+
175
+ context 'without client' do
176
+ before { layout.remove client }
177
+
178
+ it 'does not raise any error' do
179
+ expect { layout.handle_screen_set :succ }.not_to raise_error
180
+ end
181
+ end
182
+ end
183
+
184
+ describe 'handle_tag_sel' do
185
+ before { layout << client }
186
+
187
+ it 'hides clients on previously selected tag' do
188
+ layout.handle_tag_sel '2'
189
+ expect(client).to be_hidden
190
+ end
191
+
192
+ it 'sets the selected tag as the current one' do
193
+ layout.handle_tag_sel '2'
194
+ expect(layout.current_tag.id).to eq '2'
195
+ end
196
+
197
+ it 'shows and hides clients in selected tag columns' do
198
+ layout.handle_tag_sel '2'
199
+ expect(layout.current_screen.tags[0].columns)
200
+ .to all receive :show_hide_clients
201
+ layout.handle_tag_sel '1'
202
+ end
203
+
204
+ it 'focuses selected tag current client' do
205
+ layout.handle_tag_sel '2'
206
+ expect(client).to receive :focus
207
+ layout.handle_tag_sel '1'
208
+ end
209
+
210
+ it 'updates widgets' do
211
+ expect(widget).to receive :update
212
+ layout.handle_tag_sel '2'
213
+ end
214
+ end
215
+
216
+ describe 'handle_tag_set' do
217
+ context 'without client' do
218
+ it 'does not raise any error' do
219
+ expect { layout.handle_tag_set '2' }.not_to raise_error
220
+ end
221
+ end
222
+
223
+ context 'with a client' do
224
+ before { layout << other_client << client }
225
+
226
+ it 'removes current client from origin tag' do
227
+ origin_tag = layout.current_tag
228
+ layout.handle_tag_set '2'
229
+ expect(origin_tag).not_to include client
230
+ end
231
+
232
+ it 'shows and hides clients in current column' do
233
+ expect(layout.current_column).to receive :show_hide_clients
234
+ layout.handle_tag_set '2'
235
+ end
236
+
237
+ it 'hides current client' do
238
+ expect(client).to receive :hide
239
+ layout.handle_tag_set '2'
240
+ end
241
+
242
+ it 'adds current client to given tag' do
243
+ layout.handle_tag_set '2'
244
+ dest_tag = layout.current_screen.tags.find { |e| e.id == '2' }
245
+ expect(dest_tag).to include client
246
+ end
247
+
248
+ it 'arranges clients in given tag columns' do
249
+ layout.current_screen.tags << tag = Layout::Tag.new('2', geo)
250
+ expect(tag.current_column_or_create).to receive :arrange_clients
251
+ layout.handle_tag_set '2'
252
+ end
253
+
254
+ it 'updates widgets' do
255
+ expect(widget).to receive :update
256
+ layout.handle_tag_set '2'
257
+ end
258
+ end
259
+ end
260
+
261
+ describe '#handle_column_sel' do
262
+ context 'without client' do
263
+ it 'does not raise any error' do
264
+ expect { layout.handle_column_sel :succ }.not_to raise_error
265
+ end
266
+ end
267
+
268
+ context 'with two clients in two columns' do
269
+ before do
270
+ layout << client
271
+ layout.current_tag.columns << Layout::Column.new(geo).tap do |o|
272
+ o << other_client
273
+ end
274
+ end
275
+
276
+ it 'selects the column consecutive to current one in given direction' do
277
+ layout.handle_column_sel :succ
278
+ expect(layout.current_column).to be layout.current_tag.columns[1]
279
+ end
280
+
281
+ it 'focuses the current client of selected column' do
282
+ expect(other_client).to receive :focus
283
+ layout.handle_column_sel :succ
284
+ end
285
+
286
+ it 'updates widgets' do
287
+ expect(widget).to receive :update
288
+ layout.handle_column_sel :succ
289
+ end
290
+ end
291
+ end
292
+
293
+ describe '#handle_client_sel' do
294
+ context 'without client' do
295
+ it 'does not raise any error' do
296
+ expect { layout.handle_client_sel :succ }.not_to raise_error
297
+ end
298
+ end
299
+
300
+ context 'with one column and two clients' do
301
+ before { layout << client << other_client }
302
+
303
+ it 'selects current column consecutive client in given direction' do
304
+ expect { layout.handle_client_sel :pred }
305
+ .to change { layout.current_client }.from(other_client).to(client)
306
+ end
307
+
308
+ it 'focuses current client' do
309
+ expect(client).to receive :focus
310
+ layout.handle_client_sel :pred
311
+ end
312
+
313
+ it 'updates column clients visibility' do
314
+ expect(layout.current_column).to receive :show_hide_clients
315
+ layout.handle_client_sel :pred
316
+ end
317
+
318
+ it 'updates widgets' do
319
+ expect(widget).to receive :update
320
+ layout.handle_client_sel :pred
321
+ end
322
+ end
323
+ end
324
+
325
+ describe '#handle_client_swap' do
326
+ context 'without client' do
327
+ it 'does not raise any error' do
328
+ expect { layout.handle_client_swap :pred }.not_to raise_error
329
+ end
330
+ end
331
+
332
+ context 'with one column and two clients' do
333
+ before { layout << other_client << client }
334
+
335
+ it 'swaps current client with the other client' do
336
+ layout.handle_client_swap :pred
337
+ expect(layout.current_column.clients.to_a)
338
+ .to eq [client, other_client]
339
+ end
340
+
341
+ it 'does not change current client' do
342
+ expect { layout.handle_client_swap :pred }
343
+ .not_to change { layout.current_client }
344
+ end
345
+
346
+ it 'updates widgets' do
347
+ expect(widget).to receive :update
348
+ layout.handle_client_swap :pred
349
+ end
350
+ end
351
+ end
352
+
353
+ describe '#handle_client_column_set' do
354
+ context 'without client' do
355
+ it 'does not raise any error' do
356
+ expect { layout.handle_client_column_set :succ }.not_to raise_error
357
+ end
358
+ end
359
+
360
+ context 'with one column and two clients' do
361
+ let(:arranger) { instance_spy Layout::Column::Arranger }
362
+
363
+ before { layout << other_client << client }
364
+
365
+ it 'moves current client with column arranger' do
366
+ expect(arranger).to receive(:move_current_client).with(:succ)
367
+ layout.handle_client_column_set :succ, arranger: arranger
368
+ end
369
+
370
+ it 'updates columns geos with column arranger' do
371
+ expect(arranger).to receive :update_geos
372
+ layout.handle_client_column_set :succ, arranger: arranger
373
+ end
374
+
375
+ it 'arranges clients in current tag columns' do
376
+ expect(layout.current_tag.columns).to all receive :arrange_clients
377
+ layout.handle_client_column_set :succ
378
+ end
379
+
380
+ it 'shows and hides clients in selected tag columns' do
381
+ expect(layout.current_tag.columns).to all receive :show_hide_clients
382
+ layout.handle_client_column_set :succ
383
+ end
384
+
385
+ it 'does not change current client' do
386
+ expect { layout.handle_client_column_set :succ }
387
+ .not_to change { layout.current_client }
388
+ end
389
+
390
+ it 'updates widgets' do
391
+ expect(widget).to receive :update
392
+ layout.handle_client_column_set :succ
393
+ end
394
+ end
395
+ end
396
+
397
+ describe '#handle_kill_current' do
398
+ context 'without client' do
399
+ it 'does not raise any error' do
400
+ expect { layout.handle_kill_current }.not_to raise_error
401
+ end
402
+ end
403
+
404
+ context 'with a client' do
405
+ before { layout << client }
406
+
407
+ it 'kills current client' do
408
+ expect(client).to receive :kill
409
+ layout.handle_kill_current
410
+ end
411
+ end
412
+ end
413
+ end
414
+ end