vedeu 0.2.1 → 0.2.2

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +1 -2
  4. data/Rakefile +5 -5
  5. data/bin/vedeu +8 -4
  6. data/docs/api.md +3 -1
  7. data/docs/events.md +43 -29
  8. data/docs/getting_started.md +2 -0
  9. data/examples/cursor_app/cursor_app.rb +85 -0
  10. data/examples/lines_app/lines_app.rb +60 -0
  11. data/lib/vedeu.rb +9 -3
  12. data/lib/vedeu/api/api.rb +28 -7
  13. data/lib/vedeu/api/composition.rb +2 -0
  14. data/lib/vedeu/api/helpers.rb +2 -0
  15. data/lib/vedeu/api/interface.rb +2 -20
  16. data/lib/vedeu/api/keymap.rb +2 -0
  17. data/lib/vedeu/api/line.rb +2 -0
  18. data/lib/vedeu/api/menu.rb +3 -1
  19. data/lib/vedeu/api/stream.rb +25 -2
  20. data/lib/vedeu/application.rb +4 -4
  21. data/lib/vedeu/configuration/api.rb +327 -0
  22. data/lib/vedeu/configuration/cli.rb +110 -0
  23. data/lib/vedeu/{configuration.rb → configuration/configuration.rb} +49 -99
  24. data/lib/vedeu/launcher.rb +0 -1
  25. data/lib/vedeu/models/attributes/colour_translator.rb +2 -2
  26. data/lib/vedeu/models/colour.rb +0 -1
  27. data/lib/vedeu/models/composition.rb +2 -8
  28. data/lib/vedeu/models/cursor.rb +261 -0
  29. data/lib/vedeu/models/geometry.rb +11 -19
  30. data/lib/vedeu/models/interface.rb +2 -12
  31. data/lib/vedeu/models/keymap.rb +2 -0
  32. data/lib/vedeu/models/line.rb +2 -0
  33. data/lib/vedeu/models/stream.rb +2 -0
  34. data/lib/vedeu/models/style.rb +2 -0
  35. data/lib/vedeu/output/clear.rb +0 -1
  36. data/lib/vedeu/output/compositor.rb +0 -1
  37. data/lib/vedeu/output/refresh.rb +3 -0
  38. data/lib/vedeu/output/render.rb +12 -6
  39. data/lib/vedeu/output/view.rb +1 -0
  40. data/lib/vedeu/repositories/cursors.rb +98 -0
  41. data/lib/vedeu/repositories/events.rb +0 -1
  42. data/lib/vedeu/repositories/focus.rb +18 -0
  43. data/lib/vedeu/repositories/menus.rb +4 -4
  44. data/lib/vedeu/support/common.rb +2 -1
  45. data/lib/vedeu/support/event.rb +1 -10
  46. data/lib/vedeu/support/grid.rb +1 -2
  47. data/lib/vedeu/{repositories → support}/keymap_validator.rb +5 -4
  48. data/lib/vedeu/support/log.rb +3 -0
  49. data/lib/vedeu/support/menu.rb +4 -1
  50. data/lib/vedeu/support/registrar.rb +2 -1
  51. data/test/integration/defining_interfaces_test.rb +0 -1
  52. data/test/integration/views/basic_view_test.rb +741 -739
  53. data/test/lib/vedeu/api/api_test.rb +14 -3
  54. data/test/lib/vedeu/api/helpers_test.rb +3 -3
  55. data/test/lib/vedeu/api/interface_test.rb +17 -70
  56. data/test/lib/vedeu/api/keymap_test.rb +2 -0
  57. data/test/lib/vedeu/api/line_test.rb +4 -4
  58. data/test/lib/vedeu/api/menu_test.rb +6 -5
  59. data/test/lib/vedeu/api/stream_test.rb +18 -0
  60. data/test/lib/vedeu/configuration/api_test.rb +248 -0
  61. data/test/lib/vedeu/configuration/cli_test.rb +88 -0
  62. data/test/lib/vedeu/configuration/configuration_test.rb +67 -0
  63. data/test/lib/vedeu/input/input_test.rb +2 -2
  64. data/test/lib/vedeu/models/attributes/background_test.rb +3 -3
  65. data/test/lib/vedeu/models/attributes/foreground_test.rb +3 -3
  66. data/test/lib/vedeu/models/composition_test.rb +0 -222
  67. data/test/lib/vedeu/models/cursor_test.rb +164 -0
  68. data/test/lib/vedeu/models/interface_test.rb +0 -11
  69. data/test/lib/vedeu/output/compositor_test.rb +2 -4
  70. data/test/lib/vedeu/output/render_test.rb +4 -41
  71. data/test/lib/vedeu/repositories/cursors_test.rb +13 -0
  72. data/test/lib/vedeu/repositories/focus_test.rb +14 -4
  73. data/test/lib/vedeu/repositories/menus_test.rb +36 -29
  74. data/test/lib/vedeu/{repositories → support}/keymap_validator_test.rb +0 -0
  75. data/test/lib/vedeu/support/menu_test.rb +3 -3
  76. data/test/lib/vedeu/support/registrar_test.rb +6 -0
  77. data/test/lib/vedeu/support/terminal_test.rb +2 -2
  78. data/test/test_helper.rb +1 -1
  79. data/vedeu.gemspec +1 -1
  80. metadata +23 -14
  81. data/elements.txt +0 -118
  82. data/lib/vedeu/support/cursor.rb +0 -96
  83. data/test/lib/vedeu/configuration_test.rb +0 -154
  84. data/test/lib/vedeu/support/cursor_test.rb +0 -79
  85. data/test/support/model_test_data.json +0 -437
@@ -0,0 +1,88 @@
1
+ require 'test_helper'
2
+
3
+ module Vedeu
4
+ module Configuration
5
+
6
+ describe CLI do
7
+
8
+ # describe '#colour_mode' do
9
+ # it '--colour-mode' do
10
+ # Configuration.configure(['--colour-mode', '16777216'])
11
+ # Configuration.colour_mode.must_equal(16777216)
12
+ # end
13
+
14
+ # it '--colour-mode' do
15
+ # Configuration.configure(['--colour-mode', '256'])
16
+ # Configuration.colour_mode.must_equal(256)
17
+ # end
18
+
19
+ # it '--colour-mode' do
20
+ # Configuration.configure(['--colour-mode', '16'])
21
+ # Configuration.colour_mode.must_equal(16)
22
+ # end
23
+
24
+ # it '--colour-mode' do
25
+ # Configuration.configure(['--colour-mode', '348'])
26
+ # Configuration.colour_mode.must_equal(8)
27
+ # end
28
+ # end
29
+
30
+ # describe '#debug?' do
31
+ # it '--debug' do
32
+ # Configuration.configure(['--debug'])
33
+ # Configuration.debug?.must_equal(true)
34
+ # end
35
+ # end
36
+
37
+ # describe '#interactive?' do
38
+ # it '--interactive' do
39
+ # Configuration.configure(['--interactive'])
40
+ # Configuration.interactive?.must_equal(true)
41
+ # end
42
+
43
+ # it '--noninteractive' do
44
+ # Configuration.configure(['--noninteractive'])
45
+ # Configuration.interactive?.must_equal(false)
46
+ # end
47
+
48
+ # it '--standalone' do
49
+ # Configuration.configure(['--standalone'])
50
+ # Configuration.interactive?.must_equal(false)
51
+ # end
52
+ # end
53
+
54
+ # describe '#once?' do
55
+ # it '--run-once' do
56
+ # Configuration.configure(['--run-once'])
57
+ # Configuration.once?.must_equal(true)
58
+ # end
59
+
60
+ # it '--run-many' do
61
+ # Configuration.configure(['--run-many'])
62
+ # Configuration.once?.must_equal(false)
63
+ # end
64
+ # end
65
+
66
+ # describe '#terminal_mode' do
67
+ # it '--cooked' do
68
+ # Configuration.configure(['--cooked'])
69
+ # Configuration.terminal_mode.must_equal(:cooked)
70
+ # end
71
+
72
+ # it '--raw' do
73
+ # Configuration.configure(['--raw'])
74
+ # Configuration.terminal_mode.must_equal(:raw)
75
+ # end
76
+ # end
77
+
78
+ # describe '#trace?' do
79
+ # it '--trace' do
80
+ # Configuration.configure(['--trace'])
81
+ # Configuration.trace?.must_equal(true)
82
+ # end
83
+ # end
84
+
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ module Vedeu
4
+ describe Configuration do
5
+ before { Configuration.reset }
6
+ after { Configuration.reset }
7
+
8
+ describe '#colour_mode' do
9
+ it 'returns the value of the colour_mode option' do
10
+ ENV['VEDEU_TERM'] = 'xterm-truecolor'
11
+ Configuration.colour_mode.must_equal(16777216)
12
+ end
13
+ end
14
+
15
+ describe '#debug?' do
16
+ it 'returns the value of the debug option' do
17
+ Configuration.debug?.must_equal(false)
18
+ end
19
+ end
20
+
21
+ describe '#interactive?' do
22
+ it 'returns the value of the interactive option' do
23
+ Configuration.interactive?.must_equal(true)
24
+ end
25
+ end
26
+
27
+ describe '#once?' do
28
+ it 'returns the value of the once option' do
29
+ Configuration.once?.must_equal(false)
30
+ end
31
+ end
32
+
33
+ describe '#terminal_mode' do
34
+ it 'returns the value of the mode option' do
35
+ Configuration.terminal_mode.must_equal(:raw)
36
+ end
37
+ end
38
+
39
+ describe '#trace?' do
40
+ it 'returns the value of the trace option' do
41
+ Configuration.trace?.must_equal(false)
42
+ end
43
+ end
44
+
45
+ describe '.configure' do
46
+ it 'returns the options configured' do
47
+ Configuration.configure.must_equal(
48
+ {
49
+ colour_mode: 16777216,
50
+ debug: false,
51
+ interactive: true,
52
+ once: false,
53
+ system_keys: {
54
+ exit: "q",
55
+ focus_next: :tab,
56
+ focus_prev: :shift_tab,
57
+ mode_switch: :escape
58
+ },
59
+ terminal_mode: :raw,
60
+ trace: false
61
+ }
62
+ )
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -54,8 +54,8 @@ module Vedeu
54
54
  end
55
55
 
56
56
  it 'switches the terminal mode when escape is pressed' do
57
- skip # TODO: Fix this; it intermittently fails because the event isn't
58
- # registered some of the time.
57
+ skip "Fix this; it intermittently fails because the event isn't " \
58
+ "registered some of the time."
59
59
  Terminal.stub :input, "\e" do
60
60
  Vedeu.stub :log, nil do
61
61
  proc { Input.capture }.must_raise(ModeSwitch)
@@ -16,7 +16,7 @@ module Vedeu
16
16
  magenta: "\e[45m",
17
17
  white: "\e[47m",
18
18
  default: "\e[49m",
19
- unknown: "",
19
+ unknown: '',
20
20
  }.map do |colour, result|
21
21
  it "returns the correct escape sequence for #{colour}" do
22
22
  Background.escape_sequence(colour).must_equal(result)
@@ -32,8 +32,8 @@ module Vedeu
32
32
  118 => "\e[48;5;118m",
33
33
  143 => "\e[48;5;143m",
34
34
  219 => "\e[48;5;219m",
35
- -2 => "",
36
- 442 => ""
35
+ -2 => '',
36
+ 442 => ''
37
37
  }.map do |colour, result|
38
38
  it "returns the correct escape sequence for #{colour}" do
39
39
  Background.escape_sequence(colour).must_equal(result)
@@ -16,7 +16,7 @@ module Vedeu
16
16
  magenta: "\e[35m",
17
17
  white: "\e[37m",
18
18
  default: "\e[39m",
19
- unknown: "",
19
+ unknown: '',
20
20
  }.map do |colour, result|
21
21
  it "returns the correct escape sequence for #{colour}" do
22
22
  Foreground.escape_sequence(colour).must_equal(result)
@@ -32,8 +32,8 @@ module Vedeu
32
32
  118 => "\e[38;5;118m",
33
33
  143 => "\e[38;5;143m",
34
34
  219 => "\e[38;5;219m",
35
- -2 => "",
36
- 442 => ""
35
+ -2 => '',
36
+ 442 => ''
37
37
  }.map do |colour, result|
38
38
  it "returns the correct escape sequence for #{colour}" do
39
39
  Foreground.escape_sequence(colour).must_equal(result)
@@ -4,9 +4,6 @@ require 'json'
4
4
 
5
5
  module Vedeu
6
6
  describe Composition do
7
- let(:json) { File.read('test/support/model_test_data.json') }
8
- let(:attributes) { JSON.load(json, nil, symbolize_names: true) }
9
-
10
7
  before { Buffers.reset }
11
8
 
12
9
  describe '#initialize' do
@@ -36,224 +33,5 @@ module Vedeu
36
33
  end
37
34
  end
38
35
 
39
- # describe '#to_s' do
40
- # it 'returns the stringified content for a single interface, single line, single stream' do
41
- # Vedeu.interface('int1_lin1_str1') do
42
- # y 3
43
- # x 3
44
- # width 15
45
- # height 3
46
- # end
47
-
48
- # Composition.new(attributes[:test_0001]).to_s.must_equal(
49
- # "\e[3;3H \e[3;3H" \
50
- # "\e[4;3H \e[4;3H" \
51
- # "\e[5;3H \e[5;3H" \
52
- # "\e[3;3HSome text...\e[?25h"
53
- # )
54
- # end
55
-
56
- # it 'returns the stringified content for a single interface, single line, multiple streams' do
57
- # Vedeu.interface('int1_lin1_str3') do
58
- # y 3
59
- # x 3
60
- # width 30
61
- # height 3
62
- # end
63
-
64
- # Composition.new(attributes[:test_0002]).to_s.must_equal(
65
- # "\e[3;3H \e[3;3H" \
66
- # "\e[4;3H \e[4;3H" \
67
- # "\e[5;3H \e[5;3H" \
68
- # "\e[3;3HSome text... more text...\e[?25h"
69
- # )
70
- # end
71
-
72
- # it 'returns the stringified content for a single interface, multiple lines, single stream' do
73
- # Vedeu.interface('int1_lin2_str1') do
74
- # y 3
75
- # x 3
76
- # width 15
77
- # height 3
78
- # end
79
-
80
- # Composition.new(attributes[:test_0003]).to_s.must_equal(
81
- # "\e[3;3H \e[3;3H" \
82
- # "\e[4;3H \e[4;3H" \
83
- # "\e[5;3H \e[5;3H" \
84
- # "\e[3;3HSome text..." \
85
- # "\e[4;3HSome text...\e[?25h"
86
- # )
87
- # end
88
-
89
- # it 'returns the stringified content for a single interface, multiple lines, multiple streams' do
90
- # Vedeu.interface('int1_lin2_str3') do
91
- # y 3
92
- # x 3
93
- # width 30
94
- # height 3
95
- # end
96
-
97
- # Composition.new(attributes[:test_0004]).to_s.must_equal(
98
- # "\e[3;3H \e[3;3H" \
99
- # "\e[4;3H \e[4;3H" \
100
- # "\e[5;3H \e[5;3H" \
101
- # "\e[3;3HSome text... more text..." \
102
- # "\e[4;3HSome text... more text...\e[?25h"
103
- # )
104
- # end
105
-
106
- # it 'returns the stringified content for a single interface, multiple lines, multiple streams, streams contain styles' do
107
- # Vedeu.interface('int1_lin2_str3_styles') do
108
- # y 3
109
- # x 3
110
- # width 30
111
- # height 3
112
- # end
113
-
114
- # Composition.new(attributes[:test_0005]).to_s.must_equal(
115
- # "\e[3;3H \e[3;3H" \
116
- # "\e[4;3H \e[4;3H" \
117
- # "\e[5;3H \e[5;3H" \
118
- # "\e[3;3H\e[38;2;0;0;0m\e[48;2;0;0;255m\e[24m\e[22m\e[27m\e[4mSome text...\e[38;2;255;255;0m\e[48;2;0;255;0m\e[24m\e[22m\e[27m \e[38;2;255;255;255m\e[48;2;255;0;255m\e[1mmore text...\e[?25h"
119
- # )
120
- # end
121
-
122
- # it 'returns the stringified content for multiple interfaces, single line, single stream' do
123
- # Vedeu.interface('int2_lin1_str1_1') do
124
- # y 3
125
- # x 3
126
- # width 15
127
- # height 3
128
- # end
129
- # Vedeu.interface('int2_lin1_str1_2') do
130
- # y 6
131
- # x 6
132
- # width 15
133
- # height 3
134
- # end
135
-
136
- # Composition.new(attributes[:test_0006]).to_s.must_equal(
137
- # "\e[3;3H \e[3;3H" \
138
- # "\e[4;3H \e[4;3H" \
139
- # "\e[5;3H \e[5;3H" \
140
- # "\e[3;3HSome text...\e[?25h" \
141
- # "\e[6;6H \e[6;6H" \
142
- # "\e[7;6H \e[7;6H" \
143
- # "\e[8;6H \e[8;6H" \
144
- # "\e[6;6HSome text...\e[?25h"
145
- # )
146
- # end
147
-
148
- # it 'returns the stringified content for multiple interfaces, single line, multiple streams' do
149
- # Vedeu.interface('int2_lin1_str3_1') do
150
- # y 3
151
- # x 3
152
- # width 30
153
- # height 3
154
- # end
155
- # Vedeu.interface('int2_lin1_str3_2') do
156
- # y 6
157
- # x 6
158
- # width 30
159
- # height 3
160
- # end
161
-
162
- # Composition.new(attributes[:test_0007]).to_s.must_equal(
163
- # "\e[3;3H \e[3;3H" \
164
- # "\e[4;3H \e[4;3H" \
165
- # "\e[5;3H \e[5;3H" \
166
- # "\e[3;3HSome text... more text...\e[?25h" \
167
- # "\e[6;6H \e[6;6H" \
168
- # "\e[7;6H \e[7;6H" \
169
- # "\e[8;6H \e[8;6H" \
170
- # "\e[6;6HSome text... more text...\e[?25h"
171
- # )
172
- # end
173
-
174
- # it 'returns the stringified content for multiple interfaces, multiple lines, single stream' do
175
- # Vedeu.interface('int2_lin2_str1_1') do
176
- # y 3
177
- # x 3
178
- # width 15
179
- # height 3
180
- # end
181
- # Vedeu.interface('int2_lin2_str1_2') do
182
- # y 6
183
- # x 6
184
- # width 15
185
- # height 3
186
- # end
187
-
188
- # Composition.new(attributes[:test_0008]).to_s.must_equal(
189
- # "\e[3;3H \e[3;3H" \
190
- # "\e[4;3H \e[4;3H" \
191
- # "\e[5;3H \e[5;3H" \
192
- # "\e[3;3HSome text..." \
193
- # "\e[4;3HSome text...\e[?25h" \
194
- # "\e[3;3H \e[3;3H" \
195
- # "\e[4;3H \e[4;3H" \
196
- # "\e[5;3H \e[5;3H" \
197
- # "\e[3;3HSome text..." \
198
- # "\e[4;3HSome text...\e[?25h"
199
- # )
200
- # end
201
-
202
- # it 'returns the stringified content for multiple interfaces, multiple lines, multiple streams' do
203
- # Vedeu.interface('int2_lin2_str3_1') do
204
- # y 3
205
- # x 3
206
- # width 30
207
- # height 3
208
- # end
209
- # Vedeu.interface('int2_lin2_str3_2') do
210
- # y 6
211
- # x 6
212
- # width 30
213
- # height 3
214
- # end
215
-
216
- # Composition.new(attributes[:test_0009]).to_s.must_equal(
217
- # "\e[3;3H \e[3;3H" \
218
- # "\e[4;3H \e[4;3H" \
219
- # "\e[5;3H \e[5;3H" \
220
- # "\e[3;3HSome text... more text..." \
221
- # "\e[4;3HSome text... more text...\e[?25h" \
222
- # "\e[6;6H \e[6;6H" \
223
- # "\e[7;6H \e[7;6H" \
224
- # "\e[8;6H \e[8;6H" \
225
- # "\e[6;6HSome text... more text..." \
226
- # "\e[7;6HSome text... more text...\e[?25h"
227
- # )
228
- # end
229
-
230
- # it 'returns the stringified content for multiple interfaces, multiple lines, multiple streams, streams contain styles' do
231
- # Vedeu.interface('int2_lin2_str3_styles_1') do
232
- # y 3
233
- # x 3
234
- # width 30
235
- # height 3
236
- # end
237
- # Vedeu.interface('int2_lin2_str3_styles_2') do
238
- # y 6
239
- # x 6
240
- # width 30
241
- # height 3
242
- # end
243
-
244
- # Composition.new(attributes[:test_0010]).to_s.must_equal(
245
- # "\e[3;3H \e[3;3H" \
246
- # "\e[4;3H \e[4;3H" \
247
- # "\e[5;3H \e[5;3H" \
248
- # "\e[3;3H\e[38;2;0;0;0m\e[48;2;0;0;255m\e[24m\e[22m\e[27m\e[4mSome text...\e[38;2;255;255;0m\e[48;2;0;255;0m\e[24m\e[22m\e[27m \e[38;2;255;255;255m\e[48;2;255;0;255m\e[1mmore text..." \
249
- # "\e[4;3H\e[38;2;0;0;0m\e[48;2;0;0;255m\e[24m\e[22m\e[27m\e[4mSome text...\e[38;2;255;255;0m\e[48;2;0;255;0m\e[24m\e[22m\e[27m \e[38;2;255;255;255m\e[48;2;255;0;255m\e[1mmore text...\e[?25h" \
250
- # "\e[6;6H \e[6;6H" \
251
- # "\e[7;6H \e[7;6H" \
252
- # "\e[8;6H \e[8;6H" \
253
- # "\e[6;6H\e[38;2;0;0;0m\e[48;2;0;0;255m\e[24m\e[22m\e[27m\e[4mSome text...\e[38;2;255;255;0m\e[48;2;0;255;0m\e[24m\e[22m\e[27m \e[38;2;255;255;255m\e[48;2;255;0;255m\e[1mmore text..." \
254
- # "\e[7;6H\e[38;2;0;0;0m\e[48;2;0;0;255m\e[24m\e[22m\e[27m\e[4mSome text...\e[38;2;255;255;0m\e[48;2;0;255;0m\e[24m\e[22m\e[27m \e[38;2;255;255;255m\e[48;2;255;0;255m\e[1mmore text...\e[?25h"
255
- # )
256
- # end
257
- # end
258
36
  end
259
37
  end
@@ -0,0 +1,164 @@
1
+ require 'test_helper'
2
+
3
+ module Vedeu
4
+ describe Cursor do
5
+ let(:attributes) {
6
+ {
7
+ name: 'silver',
8
+ state: :show,
9
+ x: 12,
10
+ y: 5,
11
+ }
12
+ }
13
+
14
+ before do
15
+ Interfaces.reset
16
+ Vedeu.interface 'silver' do
17
+ width 10
18
+ height 4
19
+ x 12
20
+ y 5
21
+ end
22
+ end
23
+
24
+ describe '#initialize' do
25
+ it 'returns an instance of itself' do
26
+ Cursor.new(attributes).must_be_instance_of(Cursor)
27
+ end
28
+ end
29
+
30
+ describe '#attributes' do
31
+ it 'returns a hash containing the attributes of the instance' do
32
+ Cursor.new(attributes).attributes.must_equal(
33
+ { name: 'silver', state: :show, x: 12, y: 5 }
34
+ )
35
+ end
36
+ end
37
+
38
+ describe '#move_up' do
39
+ it 'does not move up when already at the top' do
40
+ cursor = Cursor.new(attributes)
41
+ cursor.move_up
42
+ cursor.attributes.must_equal(
43
+ { name: 'silver', state: :show, x: 12, y: 5 }
44
+ )
45
+ end
46
+
47
+ it 'moves the cursor up' do
48
+ cursor = Cursor.new(attributes)
49
+ 3.times { cursor.move_down }
50
+ cursor.move_up
51
+ cursor.attributes.must_equal(
52
+ { name: 'silver', state: :show, x: 12, y: 7 }
53
+ )
54
+ end
55
+ end
56
+
57
+ describe '#move_down' do
58
+ it 'moves the cursor down' do
59
+ cursor = Cursor.new(attributes)
60
+ cursor.move_down
61
+ cursor.attributes.must_equal(
62
+ { name: 'silver', state: :show, x: 12, y: 6 }
63
+ )
64
+ end
65
+
66
+ it 'does not move down when already at the bottom' do
67
+ cursor = Cursor.new(attributes)
68
+ 5.times { cursor.move_down }
69
+ cursor.attributes.must_equal(
70
+ { name: 'silver', state: :show, x: 12, y: 8 }
71
+ )
72
+ end
73
+ end
74
+
75
+ describe '#move_left' do
76
+ it 'does not move left when already at leftmost' do
77
+ cursor = Cursor.new(attributes)
78
+ cursor.move_left
79
+ cursor.attributes.must_equal(
80
+ { name: 'silver', state: :show, x: 12, y: 5 }
81
+ )
82
+ end
83
+
84
+ it 'moves the cursor left' do
85
+ cursor = Cursor.new(attributes)
86
+ 3.times { cursor.move_right }
87
+ cursor.move_left
88
+ cursor.attributes.must_equal(
89
+ { name: 'silver', state: :show, x: 14, y: 5 }
90
+ )
91
+ end
92
+ end
93
+
94
+ describe '#move_right' do
95
+ it 'does not move right when already at rightmost' do
96
+ cursor = Cursor.new(attributes)
97
+ 11.times { cursor.move_right }
98
+ cursor.attributes.must_equal(
99
+ { name: 'silver', state: :show, x: 21, y: 5 }
100
+ )
101
+ end
102
+
103
+ it 'moves the cursor right' do
104
+ cursor = Cursor.new(attributes)
105
+ cursor.move_right
106
+ cursor.attributes.must_equal(
107
+ { name: 'silver', state: :show, x: 13, y: 5 }
108
+ )
109
+ end
110
+ end
111
+
112
+ describe '#show' do
113
+ it 'sets the state attribute to :show' do
114
+ cursor = Cursor.new(attributes)
115
+ cursor.hide
116
+ cursor.attributes.must_equal(
117
+ { name: 'silver', state: :hide, x: 12, y: 5 }
118
+ )
119
+ cursor.show
120
+ cursor.attributes.must_equal(
121
+ { name: 'silver', state: :show, x: 12, y: 5 }
122
+ )
123
+ end
124
+ end
125
+
126
+ describe '#hide' do
127
+ it 'sets the state attribute to :hide' do
128
+ cursor = Cursor.new(attributes)
129
+ cursor.hide
130
+ cursor.attributes.must_equal(
131
+ { name: 'silver', state: :hide, x: 12, y: 5 }
132
+ )
133
+ end
134
+ end
135
+
136
+ describe '#toggle' do
137
+ it 'sets the visibility to the opposite of its current state' do
138
+ cursor = Cursor.new(attributes)
139
+ cursor.toggle
140
+ cursor.attributes.must_equal(
141
+ { name: 'silver', state: :hide, x: 12, y: 5 }
142
+ )
143
+ cursor.toggle
144
+ cursor.attributes.must_equal(
145
+ { name: 'silver', state: :show, x: 12, y: 5 }
146
+ )
147
+ end
148
+ end
149
+
150
+ describe '#to_s' do
151
+ it 'returns the escape sequence to position and set the visibility of ' \
152
+ 'the cursor' do
153
+ Cursor.new(attributes).to_s.must_equal("\e[5;12H\e[?25h")
154
+ end
155
+
156
+ it 'returns the escape sequence to position and set the visibility of ' \
157
+ 'the cursor and returns to that position after yielding the block' do
158
+ Cursor.new(attributes).to_s do
159
+ # ...
160
+ end.must_equal("\e[5;12H\e[?25h\e[5;12H\e[?25h")
161
+ end
162
+ end
163
+ end
164
+ end