tty-prompt 0.18.1 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +174 -63
  4. data/Rakefile +2 -2
  5. data/examples/ask_blank.rb +9 -0
  6. data/examples/enum_select_disabled.rb +1 -1
  7. data/examples/enum_select_paged.rb +1 -1
  8. data/examples/expand_auto.rb +29 -0
  9. data/examples/mask.rb +1 -1
  10. data/examples/multi_select.rb +1 -1
  11. data/examples/multi_select_disabled_paged.rb +22 -0
  12. data/examples/multi_select_paged.rb +1 -1
  13. data/examples/select_disabled_paged.rb +22 -0
  14. data/examples/select_paginated.rb +1 -1
  15. data/lib/tty/prompt.rb +46 -10
  16. data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +19 -18
  17. data/lib/tty/prompt/choice.rb +1 -3
  18. data/lib/tty/prompt/enum_list.rb +31 -9
  19. data/lib/tty/prompt/expander.rb +19 -1
  20. data/lib/tty/prompt/keypress.rb +30 -35
  21. data/lib/tty/prompt/list.rb +112 -40
  22. data/lib/tty/prompt/mask_question.rb +2 -3
  23. data/lib/tty/prompt/multi_list.rb +36 -12
  24. data/lib/tty/prompt/paginator.rb +37 -25
  25. data/lib/tty/prompt/question.rb +29 -5
  26. data/lib/tty/prompt/slider.rb +16 -8
  27. data/lib/tty/prompt/symbols.rb +30 -6
  28. data/lib/tty/prompt/timer.rb +75 -0
  29. data/lib/tty/prompt/version.rb +1 -1
  30. data/spec/spec_helper.rb +18 -2
  31. data/spec/unit/ask_spec.rb +45 -4
  32. data/spec/unit/{enum_paginator_spec.rb → block_paginator_spec.rb} +14 -5
  33. data/spec/unit/choice/from_spec.rb +16 -0
  34. data/spec/unit/enum_select_spec.rb +104 -32
  35. data/spec/unit/expand_spec.rb +104 -12
  36. data/spec/unit/keypress_spec.rb +2 -8
  37. data/spec/unit/mask_spec.rb +9 -1
  38. data/spec/unit/multi_select_spec.rb +348 -118
  39. data/spec/unit/paginator_spec.rb +29 -10
  40. data/spec/unit/select_spec.rb +390 -108
  41. data/spec/unit/slider_spec.rb +48 -6
  42. data/spec/unit/timer_spec.rb +29 -0
  43. data/tty-prompt.gemspec +4 -6
  44. metadata +17 -46
  45. data/lib/tty/prompt/timeout.rb +0 -78
@@ -23,9 +23,9 @@ RSpec.describe TTY::Prompt::Paginator, '#paginate' do
23
23
 
24
24
  expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
25
25
  expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
26
- expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
27
- expect(paginator.paginate(list, 4).to_a).to eq([['b',1], ['c',2], ['d',3]])
28
- expect(paginator.paginate(list, 5).to_a).to eq([['c',2], ['d',3], ['e',4]])
26
+ expect(paginator.paginate(list, 3).to_a).to eq([['b',1], ['c',2], ['d',3]])
27
+ expect(paginator.paginate(list, 4).to_a).to eq([['c',2], ['d',3], ['e',4]])
28
+ expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
29
29
  expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
30
30
  expect(paginator.paginate(list, 7).to_a).to eq([['d',3], ['e',4], ['f',5]])
31
31
  end
@@ -36,22 +36,41 @@ RSpec.describe TTY::Prompt::Paginator, '#paginate' do
36
36
 
37
37
  expect(paginator.paginate(list, 1).to_a).to eq([['a',0], ['b',1], ['c',2]])
38
38
  expect(paginator.paginate(list, 2).to_a).to eq([['a',0], ['b',1], ['c',2]])
39
- expect(paginator.paginate(list, 3).to_a).to eq([['a',0], ['b',1], ['c',2]])
40
- expect(paginator.paginate(list, 4).to_a).to eq([['b',1], ['c',2], ['d',3]])
41
- expect(paginator.paginate(list, 5).to_a).to eq([['c',2], ['d',3], ['e',4]])
42
- expect(paginator.paginate(list, 6).to_a).to eq([['d',3], ['e',4], ['f',5]])
39
+ expect(paginator.paginate(list, 3).to_a).to eq([['b',1], ['c',2], ['d',3]])
40
+ expect(paginator.paginate(list, 4).to_a).to eq([['c',2], ['d',3], ['e',4]])
41
+ expect(paginator.paginate(list, 5).to_a).to eq([['d',3], ['e',4], ['f',5]])
42
+ expect(paginator.paginate(list, 6).to_a).to eq([['e',4], ['f',5], ['g',6]])
43
43
  expect(paginator.paginate(list, 7).to_a).to eq([['e',4], ['f',5], ['g',6]])
44
44
  expect(paginator.paginate(list, 8).to_a).to eq([['e',4], ['f',5], ['g',6]])
45
45
  end
46
46
 
47
- it "finds maximum index for current selection" do
47
+ it "finds both start and end index for current selection" do
48
48
  list = %w(a b c d e f g)
49
49
  paginator = described_class.new({per_page: 3, default: 0})
50
50
 
51
+ paginator.paginate(list, 2)
52
+ expect(paginator.start_index).to eq(0)
53
+ expect(paginator.end_index).to eq(2)
54
+
55
+ paginator.paginate(list, 3)
56
+ expect(paginator.start_index).to eq(1)
57
+ expect(paginator.end_index).to eq(3)
58
+
51
59
  paginator.paginate(list, 4)
52
- expect(paginator.max_index).to eq(3)
60
+ expect(paginator.start_index).to eq(2)
61
+ expect(paginator.end_index).to eq(4)
62
+
53
63
  paginator.paginate(list, 5)
54
- expect(paginator.max_index).to eq(4)
64
+ expect(paginator.start_index).to eq(3)
65
+ expect(paginator.end_index).to eq(5)
66
+
67
+ paginator.paginate(list, 7)
68
+ expect(paginator.start_index).to eq(4)
69
+ expect(paginator.end_index).to eq(6)
70
+
71
+ paginator.paginate(list, 8)
72
+ expect(paginator.start_index).to eq(4)
73
+ expect(paginator.end_index).to eq(6)
55
74
  end
56
75
 
57
76
  it "starts with default selection" do
@@ -5,6 +5,8 @@ RSpec.describe TTY::Prompt, '#select' do
5
5
  subject(:prompt) { TTY::TestPrompt.new }
6
6
 
7
7
  let(:symbols) { TTY::Prompt::Symbols.symbols }
8
+ let(:up_down) { "#{symbols[:arrow_up]}/#{symbols[:arrow_down]}" }
9
+ let(:left_right) { "#{symbols[:arrow_left]}/#{symbols[:arrow_right]}"}
8
10
 
9
11
  def output_helper(prompt, choices, active, options = {})
10
12
  raise ":init requires :hint" if options[:init] && options[:hint].nil?
@@ -24,7 +26,7 @@ RSpec.describe TTY::Prompt, '#select' do
24
26
  if disabled
25
27
  "\e[31m#{symbols[:cross]}\e[0m #{num}#{name} #{disabled}"
26
28
  elsif name == active
27
- "\e[32m#{symbols[:pointer]} #{num}#{name}\e[0m"
29
+ "\e[32m#{symbols[:marker]} #{num}#{name}\e[0m"
28
30
  else
29
31
  " #{num}#{name}"
30
32
  end
@@ -49,8 +51,8 @@ RSpec.describe TTY::Prompt, '#select' do
49
51
 
50
52
  expect(prompt.select('What size?', choices)).to eq(:Large)
51
53
  expected_output = [
52
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
53
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
54
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
55
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
54
56
  " Medium\n",
55
57
  " Small",
56
58
  "\e[2K\e[1G\e[1A" * 3,
@@ -70,15 +72,15 @@ RSpec.describe TTY::Prompt, '#select' do
70
72
  end
71
73
  expect { prompt.select('What size?', choices) }.not_to output.to_stderr
72
74
  expect(prompt.output.string).to eq([
73
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
74
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
75
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
76
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
75
77
  " Medium\n",
76
78
  " Small",
77
79
  "\e[2K\e[1G\e[1A" * 3,
78
80
  "\e[2K\e[1G",
79
81
  "What size? \n",
80
82
  " Large\n",
81
- "\e[32m#{symbols[:pointer]} Medium\e[0m\n",
83
+ "\e[32m#{symbols[:marker]} Medium\e[0m\n",
82
84
  " Small",
83
85
  "\e[2K\e[1G\e[1A" * 3,
84
86
  "\e[2K\e[1G",
@@ -92,8 +94,8 @@ RSpec.describe TTY::Prompt, '#select' do
92
94
  prompt.input.rewind
93
95
  expect(prompt.select('What size?', choices, default: 1)).to eq(1)
94
96
  expect(prompt.output.string).to eq([
95
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
96
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
97
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
98
+ "\e[32m#{symbols[:marker]} large\e[0m\n",
97
99
  " medium\n",
98
100
  " small",
99
101
  "\e[2K\e[1G\e[1A" * 3,
@@ -106,14 +108,16 @@ RSpec.describe TTY::Prompt, '#select' do
106
108
  prompt.input << " "
107
109
  prompt.input.rewind
108
110
  value = prompt.select('What size?') do |menu|
111
+ menu.symbols marker: '>'
112
+
109
113
  menu.choice "Large"
110
114
  menu.choice "Medium"
111
115
  menu.choice "Small"
112
116
  end
113
117
  expect(value).to eq('Large')
114
118
  expect(prompt.output.string).to eq([
115
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
116
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
119
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
120
+ "\e[32m> Large\e[0m\n",
117
121
  " Medium\n",
118
122
  " Small",
119
123
  "\e[2K\e[1G\e[1A" * 3,
@@ -123,6 +127,7 @@ RSpec.describe TTY::Prompt, '#select' do
123
127
  end
124
128
 
125
129
  it "sets choice name & value through DSL" do
130
+ prompt = TTY::TestPrompt.new(symbols: {marker: '>'})
126
131
  prompt.input << " "
127
132
  prompt.input.rewind
128
133
  value = prompt.select('What size?') do |menu|
@@ -132,8 +137,8 @@ RSpec.describe TTY::Prompt, '#select' do
132
137
  end
133
138
  expect(value).to eq(1)
134
139
  expect(prompt.output.string).to eq([
135
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
136
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
140
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
141
+ "\e[32m> large\e[0m\n",
137
142
  " medium\n",
138
143
  " small",
139
144
  "\e[2K\e[1G\e[1A" * 3,
@@ -151,8 +156,8 @@ RSpec.describe TTY::Prompt, '#select' do
151
156
  end
152
157
  expect(value).to eq('Large')
153
158
  expect(prompt.output.string).to eq([
154
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
155
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
159
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
160
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
156
161
  " Medium\n",
157
162
  " Small",
158
163
  "\e[2K\e[1G\e[1A" * 3,
@@ -174,9 +179,9 @@ RSpec.describe TTY::Prompt, '#select' do
174
179
  end
175
180
  expect(value).to eq(2)
176
181
  expect(prompt.output.string).to eq([
177
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
182
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
178
183
  " 1. large\n",
179
- "\e[32m#{symbols[:pointer]} 2. medium\e[0m\n",
184
+ "\e[32m#{symbols[:marker]} 2. medium\e[0m\n",
180
185
  " 3. small",
181
186
  "\e[2K\e[1G\e[1A" * 3,
182
187
  "\e[2K\e[1G",
@@ -192,11 +197,12 @@ RSpec.describe TTY::Prompt, '#select' do
192
197
  menu.choice :medium do 'Good choice!' end
193
198
  menu.choice :small, 3
194
199
  end
200
+
195
201
  expect(value).to eq('Good choice!')
196
202
  expect(prompt.output.string).to eq([
197
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
203
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
198
204
  " 1) large\n",
199
- "\e[32m#{symbols[:pointer]} 2) medium\e[0m\n",
205
+ "\e[32m#{symbols[:marker]} 2) medium\e[0m\n",
200
206
  " 3) small",
201
207
  "\e[2K\e[1G\e[1A" * 3,
202
208
  "\e[2K\e[1G",
@@ -210,9 +216,9 @@ RSpec.describe TTY::Prompt, '#select' do
210
216
  prompt.input.rewind
211
217
  expect(prompt.select('What size?', choices, default: 2, enum: '.')).to eq('Medium')
212
218
  expect(prompt.output.string).to eq([
213
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
219
+ "\e[?25lWhat size? \e[90m(Use #{up_down} arrow or number (1-3) keys, press Enter to select)\e[0m\n",
214
220
  " 1. Large\n",
215
- "\e[32m#{symbols[:pointer]} 2. Medium\e[0m\n",
221
+ "\e[32m#{symbols[:marker]} 2. Medium\e[0m\n",
216
222
  " 3. Small",
217
223
  "\e[2K\e[1G\e[1A" * 3,
218
224
  "\e[2K\e[1G",
@@ -222,13 +228,16 @@ RSpec.describe TTY::Prompt, '#select' do
222
228
 
223
229
  it "changes selected item color & marker" do
224
230
  choices = %w(Large Medium Small)
231
+ prompt = TTY::TestPrompt.new(symbols: {marker: '>'})
225
232
  prompt.input << " "
226
233
  prompt.input.rewind
227
- options = {active_color: :blue, help_color: :red, marker: '>'}
234
+ options = {active_color: :blue, help_color: :red, symbols: {marker: '>' }}
235
+
228
236
  value = prompt.select('What size?', choices, options)
237
+
229
238
  expect(value).to eq('Large')
230
239
  expect(prompt.output.string).to eq([
231
- "\e[?25lWhat size? \e[31m(Use arrow keys, press Enter to select)\e[0m\n",
240
+ "\e[?25lWhat size? \e[31m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
232
241
  "\e[34m> Large\e[0m\n",
233
242
  " Medium\n",
234
243
  " Small",
@@ -246,7 +255,7 @@ RSpec.describe TTY::Prompt, '#select' do
246
255
  expect(value).to eq('Large')
247
256
  expect(prompt.output.string).to eq([
248
257
  "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
249
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
258
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
250
259
  " Medium\n",
251
260
  " Small",
252
261
  "\e[2K\e[1G\e[1A" * 3,
@@ -266,7 +275,7 @@ RSpec.describe TTY::Prompt, '#select' do
266
275
  expect(value).to eq('Large')
267
276
  expect(prompt.output.string).to eq([
268
277
  "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
269
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
278
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
270
279
  " Medium\n",
271
280
  " Small",
272
281
  "\e[2K\e[1G\e[1A" * 3,
@@ -282,8 +291,8 @@ RSpec.describe TTY::Prompt, '#select' do
282
291
  prompt.input.rewind
283
292
  expect(prompt.select('What size?', choices)).to eq('Large')
284
293
  expect(prompt.output.string).to eq([
285
- "\e[?25l[?] What size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
286
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
294
+ "\e[?25l[?] What size? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
295
+ "\e[32m#{symbols[:marker]} Large\e[0m\n",
287
296
  " Medium\n",
288
297
  " Small",
289
298
  "\e[2K\e[1G\e[1A" * 3,
@@ -292,66 +301,258 @@ RSpec.describe TTY::Prompt, '#select' do
292
301
  ].join)
293
302
  end
294
303
 
295
- it "paginates long selections" do
296
- choices = %w(A B C D E F G H)
297
- prompt.input << "\r"
298
- prompt.input.rewind
299
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
300
- expect(value).to eq('D')
301
- expect(prompt.output.string).to eq([
302
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
303
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
304
- " E\n",
305
- " F\n",
306
- "\e[90m(Move up or down to reveal more choices)\e[0m",
307
- "\e[2K\e[1G\e[1A" * 4,
308
- "\e[2K\e[1G",
309
- "What letter? \e[32mD\e[0m\n\e[?25h",
310
- ].join)
311
- end
304
+ context "when paginated" do
305
+ it "paginates long selections" do
306
+ choices = %w(A B C D E F G H)
307
+ prompt.input << "\r"
308
+ prompt.input.rewind
312
309
 
313
- it "paginates choices as hash object" do
314
- prompt = TTY::TestPrompt.new
315
- choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
316
- prompt.input << "\r"
317
- prompt.input.rewind
318
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
319
- expect(value).to eq(4)
320
- expect(prompt.output.string).to eq([
321
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
322
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
323
- " E\n",
324
- " F\n",
325
- "\e[90m(Move up or down to reveal more choices)\e[0m",
326
- "\e[2K\e[1G\e[1A" * 4,
327
- "\e[2K\e[1G",
328
- "What letter? \e[32mD\e[0m\n\e[?25h",
329
- ].join)
330
- end
310
+ answer = prompt.select("What letter?", choices, per_page: 3, default: 4)
331
311
 
332
- it "paginates long selections through DSL" do
333
- prompt = TTY::TestPrompt.new
334
- choices = %w(A B C D E F G H)
335
- prompt.input << "\r"
336
- prompt.input.rewind
337
- value = prompt.select("What letter?") do |menu|
338
- menu.per_page 3
339
- menu.page_help '(Wiggle thy finger up or down to see more)'
340
- menu.default 4
312
+ expect(answer).to eq('D')
313
+ expected_output = [
314
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
315
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
316
+ " E\n",
317
+ " F",
318
+ "\e[2K\e[1G\e[1A" * 3,
319
+ "\e[2K\e[1G",
320
+ "What letter? \e[32mD\e[0m\n\e[?25h",
321
+ ].join
341
322
 
342
- menu.choices choices
343
- end
344
- expect(value).to eq('D')
345
- expect(prompt.output.string).to eq([
346
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
347
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
348
- " E\n",
349
- " F\n",
350
- "\e[90m(Wiggle thy finger up or down to see more)\e[0m",
351
- "\e[2K\e[1G\e[1A" * 4,
352
- "\e[2K\e[1G",
353
- "What letter? \e[32mD\e[0m\n\e[?25h",
354
- ].join)
323
+ expect(prompt.output.string).to eq(expected_output)
324
+ end
325
+
326
+ it "paginates choices as hash object" do
327
+ prompt = TTY::TestPrompt.new
328
+ choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
329
+ prompt.input << "\r"
330
+ prompt.input.rewind
331
+
332
+ answer = prompt.select("What letter?", choices, per_page: 3, default: 4)
333
+
334
+ expect(answer).to eq(4)
335
+ expected_output = [
336
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
337
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
338
+ " E\n",
339
+ " F",
340
+ "\e[2K\e[1G\e[1A" * 3,
341
+ "\e[2K\e[1G",
342
+ "What letter? \e[32mD\e[0m\n\e[?25h",
343
+ ].join
344
+ expect(prompt.output.string).to eq(expected_output)
345
+ end
346
+
347
+ it "paginates long selections through DSL" do
348
+ prompt = TTY::TestPrompt.new
349
+ choices = %w(A B C D E F G H)
350
+ prompt.input << "\r"
351
+ prompt.input.rewind
352
+
353
+ answer = prompt.select("What letter?") do |menu|
354
+ menu.per_page 3
355
+ menu.default 4
356
+
357
+ menu.choices choices
358
+ end
359
+
360
+ expect(answer).to eq('D')
361
+
362
+
363
+ expected_output = [
364
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} and #{left_right} arrow keys, press Enter to select)\e[0m\n",
365
+ "\e[32m#{symbols[:marker]} D\e[0m\n",
366
+ " E\n",
367
+ " F",
368
+ "\e[2K\e[1G\e[1A" * 3,
369
+ "\e[2K\e[1G",
370
+ "What letter? \e[32mD\e[0m\n\e[?25h",
371
+ ].join
372
+ expect(prompt.output.string).to eq(expected_output)
373
+ end
374
+
375
+ it "navigates evenly paged output with right arrow until end of selection" do
376
+ prompt = TTY::TestPrompt.new
377
+ choices = ('1'..'12').to_a
378
+ prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" }
379
+ prompt.input << "l" << "l" << "l" << "\r"
380
+ prompt.input.rewind
381
+
382
+ answer = prompt.select("What number?", choices, per_page: 4)
383
+
384
+ expect(answer).to eq('9')
385
+
386
+ expected_output = [
387
+ output_helper('What number?', choices[0..3], "1", init: true,
388
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
389
+ output_helper('What number?', choices[4..7], "5"),
390
+ output_helper('What number?', choices[8..11], "9"),
391
+ output_helper('What number?', choices[8..11], "9"),
392
+ "What number? \e[32m9\e[0m\n\e[?25h",
393
+ ].join
394
+
395
+ expect(prompt.output.string).to eq(expected_output)
396
+ end
397
+
398
+ it "navigates unevenly paged output with right arrow until the end of selection" do
399
+ prompt = TTY::TestPrompt.new
400
+ choices = ('1'..'10').to_a
401
+ prompt.on(:keypress) { |e| prompt.trigger(:keyright) if e.value == "l" }
402
+ prompt.input << "l" << "l" << "l" << "\r"
403
+ prompt.input.rewind
404
+
405
+ answer = prompt.select("What number?", choices, default: 4, per_page: 4)
406
+
407
+ expect(answer).to eq('10')
408
+
409
+ expected_output = [
410
+ output_helper('What number?', choices[3..6], "4", init: true,
411
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
412
+ output_helper('What number?', choices[4..7], "8"),
413
+ output_helper('What number?', choices[8..9], "10"),
414
+ output_helper('What number?', choices[8..9], "10"),
415
+ "What number? \e[32m10\e[0m\n\e[?25h",
416
+ ].join
417
+
418
+ expect(prompt.output.string).to eq(expected_output)
419
+ end
420
+
421
+ it "navigates left and right" do
422
+ prompt = TTY::TestPrompt.new
423
+ choices = ('1'..'10').to_a
424
+ prompt.on(:keypress) { |e|
425
+ prompt.trigger(:keyright) if e.value == "l"
426
+ prompt.trigger(:keyleft) if e.value == "h"
427
+ }
428
+ prompt.input << "l" << "l" << "h" << "\r"
429
+ prompt.input.rewind
430
+
431
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4)
432
+
433
+ expect(answer).to eq('6')
434
+
435
+ expected_output = [
436
+ output_helper('What number?', choices[0..3], "2", init: true,
437
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
438
+ output_helper('What number?', choices[4..7], "6"),
439
+ output_helper('What number?', choices[8..9], "10"),
440
+ output_helper('What number?', choices[4..7], "6"),
441
+ "What number? \e[32m6\e[0m\n\e[?25h",
442
+ ].join
443
+
444
+ expect(prompt.output.string).to eq(expected_output)
445
+ end
446
+
447
+ it "combines up/down navigation with left/right" do
448
+ prompt = TTY::TestPrompt.new
449
+ choices = ('1'..'11').to_a
450
+ prompt.on(:keypress) { |e|
451
+ prompt.trigger(:keyup) if e.value == "k"
452
+ prompt.trigger(:keydown) if e.value == "j"
453
+ prompt.trigger(:keyright) if e.value == "l"
454
+ prompt.trigger(:keyleft) if e.value == "h"
455
+ }
456
+ prompt.input << "j" << "l" << "k" << "k" << "h" << "\r"
457
+ prompt.input.rewind
458
+
459
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4)
460
+
461
+ expect(answer).to eq('1')
462
+
463
+ expected_output = [
464
+ output_helper('What number?', choices[0..3], "2", init: true,
465
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
466
+ output_helper('What number?', choices[0..3], "3"),
467
+ output_helper('What number?', choices[4..7], "7"),
468
+ output_helper('What number?', choices[4..7], "6"),
469
+ output_helper('What number?', choices[3..6], "5"),
470
+ output_helper('What number?', choices[0..3], "1"),
471
+ "What number? \e[32m1\e[0m\n\e[?25h"
472
+ ].join
473
+
474
+ expect(prompt.output.string).to eq(expected_output)
475
+ end
476
+
477
+ it "navigates pages up/down with disabled items" do
478
+ prompt = TTY::TestPrompt.new
479
+ prompt.on(:keypress) { |e|
480
+ prompt.trigger(:keyup) if e.value == "k"
481
+ prompt.trigger(:keydown) if e.value == "j"
482
+ }
483
+ choices = [
484
+ '1',
485
+ {name: '2', disabled: 'out'},
486
+ '3',
487
+ {name: '4', disabled: 'out'},
488
+ '5',
489
+ {name: '6', disabled: 'out'},
490
+ {name: '7', disabled: 'out'},
491
+ '8',
492
+ '9',
493
+ {name: '10', disabled: 'out'},
494
+ ]
495
+
496
+ prompt.input << "j" << "j" << "j" << "j" << "\r"
497
+ prompt.input.rewind
498
+
499
+ answer = prompt.select("What number?", choices, per_page: 4)
500
+
501
+ expect(answer).to eq('9')
502
+
503
+ expected_output = [
504
+ output_helper('What number?', choices[0..3], "1", init: true,
505
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
506
+ output_helper('What number?', choices[0..3], "3"),
507
+ output_helper('What number?', choices[2..5], "5"),
508
+ output_helper('What number?', choices[5..8], "8"),
509
+ output_helper('What number?', choices[6..9], "9"),
510
+ "What number? \e[32m9\e[0m\n\e[?25h"
511
+ ].join('')
512
+
513
+ expect(prompt.output.string).to eq(expected_output)
514
+ end
515
+
516
+ it "navigates pages left/right with disabled items" do
517
+ prompt = TTY::TestPrompt.new
518
+ prompt.on(:keypress) { |e|
519
+ prompt.trigger(:keyright) if e.value == "l"
520
+ prompt.trigger(:keyleft) if e.value == "h"
521
+ }
522
+ choices = [
523
+ {name: '1', disabled: 'out'},
524
+ '2',
525
+ {name: '3', disabled: 'out'},
526
+ '4',
527
+ '5',
528
+ {name: '6', disabled: 'out'},
529
+ '7',
530
+ '8',
531
+ '9',
532
+ {name: '10', disabled: 'out'}
533
+ ]
534
+
535
+ prompt.input << "l" << "l" << "l" << "h" << "h" << "h" << "\r"
536
+ prompt.input.rewind
537
+
538
+ answer = prompt.select("What number?", choices, per_page: 4)
539
+
540
+ expect(answer).to eq('2')
541
+
542
+ expected_output = [
543
+ output_helper('What number?', choices[0..3], "2", init: true,
544
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
545
+ output_helper('What number?', choices[4..7], "7"),
546
+ output_helper('What number?', choices[8..9], "9"),
547
+ output_helper('What number?', choices[8..9], "9"),
548
+ output_helper('What number?', choices[4..7], "5"),
549
+ output_helper('What number?', choices[0..3], "2"),
550
+ output_helper('What number?', choices[0..3], "2"),
551
+ "What number? \e[32m2\e[0m\n\e[?25h"
552
+ ].join('')
553
+
554
+ expect(prompt.output.string).to eq(expected_output)
555
+ end
355
556
  end
356
557
 
357
558
  context 'with :cycle option' do
@@ -361,15 +562,19 @@ RSpec.describe TTY::Prompt, '#select' do
361
562
  prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
362
563
  prompt.input << "j" << "j" << "j" << "\r"
363
564
  prompt.input.rewind
565
+
364
566
  value = prompt.select("What letter?", choices)
567
+
365
568
  expect(value).to eq("C")
366
- expect(prompt.output.string).to eq(
367
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
368
- output_helper("What letter?", choices, "B") +
369
- output_helper("What letter?", choices, "C") +
370
- output_helper("What letter?", choices, "C") +
569
+ expected_output = [
570
+ output_helper("What letter?", choices, "A", init: true,
571
+ hint: "Use #{up_down} arrow keys, press Enter to select"),
572
+ output_helper("What letter?", choices, "B"),
573
+ output_helper("What letter?", choices, "C"),
574
+ output_helper("What letter?", choices, "C"),
371
575
  "What letter? \e[32mC\e[0m\n\e[?25h"
372
- )
576
+ ].join
577
+ expect(prompt.output.string).to eq(expected_output)
373
578
  end
374
579
 
375
580
  it "cycles around when configured to do so" do
@@ -378,15 +583,19 @@ RSpec.describe TTY::Prompt, '#select' do
378
583
  prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
379
584
  prompt.input << "j" << "j" << "j" << "\r"
380
585
  prompt.input.rewind
381
- value = prompt.select("What letter?", choices, cycle: true)
382
- expect(value).to eq("A")
383
- expect(prompt.output.string).to eq(
384
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
385
- output_helper("What letter?", choices, "B") +
386
- output_helper("What letter?", choices, "C") +
387
- output_helper("What letter?", choices, "A") +
586
+
587
+ answer = prompt.select("What letter?", choices, cycle: true)
588
+
589
+ expect(answer).to eq("A")
590
+ expected_output = [
591
+ output_helper("What letter?", choices, "A", init: true,
592
+ hint: "Use #{up_down} arrow keys, press Enter to select"),
593
+ output_helper("What letter?", choices, "B"),
594
+ output_helper("What letter?", choices, "C"),
595
+ output_helper("What letter?", choices, "A"),
388
596
  "What letter? \e[32mA\e[0m\n\e[?25h"
389
- )
597
+ ].join
598
+ expect(prompt.output.string).to eq(expected_output)
390
599
  end
391
600
 
392
601
  it "cycles around disabled items" do
@@ -406,7 +615,7 @@ RSpec.describe TTY::Prompt, '#select' do
406
615
 
407
616
  expected_output =
408
617
  output_helper("What letter?", choices, "B", init: true,
409
- hint: "Use arrow keys, press Enter to select") +
618
+ hint: "Use #{up_down} arrow keys, press Enter to select") +
410
619
  output_helper("What letter?", choices, "D") +
411
620
  output_helper("What letter?", choices, "B") +
412
621
  output_helper("What letter?", choices, "D") +
@@ -414,6 +623,74 @@ RSpec.describe TTY::Prompt, '#select' do
414
623
 
415
624
  expect(prompt.output.string).to eq(expected_output)
416
625
  end
626
+
627
+ it "cycles choices using left/right arrows" do
628
+ prompt = TTY::TestPrompt.new
629
+ choices = ('1'..'10').to_a
630
+ prompt.on(:keypress) { |e|
631
+ prompt.trigger(:keyright) if e.value == "l"
632
+ prompt.trigger(:keyleft) if e.value == "h"
633
+ }
634
+ prompt.input << "l" << "l" << "l" << "h" << "\r"
635
+ prompt.input.rewind
636
+
637
+ answer = prompt.select("What number?", choices, default: 2, per_page: 4, cycle: true)
638
+
639
+ expect(answer).to eq('10')
640
+
641
+ expected_output = [
642
+ output_helper('What number?', choices[0..3], "2", init: true,
643
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
644
+ output_helper('What number?', choices[4..7], "6"),
645
+ output_helper('What number?', choices[8..9], "10"),
646
+ output_helper('What number?', choices[0..3], "2"),
647
+ output_helper('What number?', choices[8..9], "10"),
648
+ "What number? \e[32m10\e[0m\n\e[?25h",
649
+ ].join
650
+
651
+ expect(prompt.output.string).to eq(expected_output)
652
+ end
653
+
654
+ it "cycles pages left/right with disabled items" do
655
+ prompt = TTY::TestPrompt.new
656
+ prompt.on(:keypress) { |e|
657
+ prompt.trigger(:keyright) if e.value == "l"
658
+ prompt.trigger(:keyleft) if e.value == "h"
659
+ }
660
+ choices = [
661
+ {name: '1', disabled: 'out'},
662
+ '2',
663
+ {name: '3', disabled: 'out'},
664
+ '4',
665
+ '5',
666
+ {name: '6', disabled: 'out'},
667
+ '7',
668
+ '8',
669
+ '9',
670
+ {name: '10', disabled: 'out'}
671
+ ]
672
+
673
+ prompt.input << "l" << "l" << "l" << "h" << "h" << "h" << "\r"
674
+ prompt.input.rewind
675
+
676
+ answer = prompt.select("What number?", choices, per_page: 4, cycle: true)
677
+
678
+ expect(answer).to eq('2')
679
+
680
+ expected_output = [
681
+ output_helper('What number?', choices[0..3], "2", init: true,
682
+ hint: "Use #{up_down} and #{left_right} arrow keys, press Enter to select"),
683
+ output_helper('What number?', choices[4..7], "7"),
684
+ output_helper('What number?', choices[8..9], "9"),
685
+ output_helper('What number?', choices[0..3], "2"),
686
+ output_helper('What number?', choices[8..9], "9"),
687
+ output_helper('What number?', choices[4..7], "5"),
688
+ output_helper('What number?', choices[0..3], "2"),
689
+ "What number? \e[32m2\e[0m\n\e[?25h"
690
+ ].join('')
691
+
692
+ expect(prompt.output.string).to eq(expected_output)
693
+ end
417
694
  end
418
695
 
419
696
  it "verifies default index format" do
@@ -436,8 +713,8 @@ RSpec.describe TTY::Prompt, '#select' do
436
713
  expect(value).to eq('A')
437
714
 
438
715
  expect(prompt.output.string).to eq([
439
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
440
- "\e[32m#{symbols[:pointer]} A\e[0m\n",
716
+ "\e[?25lWhat letter? \e[90m(Use #{up_down} arrow keys, press Enter to select)\e[0m\n",
717
+ "\e[32m#{symbols[:marker]} A\e[0m\n",
441
718
  " B\n",
442
719
  " C\n",
443
720
  " D",
@@ -478,7 +755,8 @@ RSpec.describe TTY::Prompt, '#select' do
478
755
  actual_prompt_output = prompt.output.string
479
756
 
480
757
  expected_prompt_output =
481
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
758
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
759
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
482
760
  output_helper("What size?", %w(Medium Huge), "Medium", hint: 'Filter: "U"') +
483
761
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Ug"') +
484
762
  exit_message("What size?", "Huge")
@@ -496,7 +774,8 @@ RSpec.describe TTY::Prompt, '#select' do
496
774
 
497
775
  actual_prompt_output = prompt.output.string
498
776
  expected_prompt_output =
499
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
777
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
778
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
500
779
  output_helper("What size?", %w(Large Huge), "Large", hint: 'Filter: "g"') +
501
780
  exit_message("What size?", "Large")
502
781
 
@@ -513,7 +792,8 @@ RSpec.describe TTY::Prompt, '#select' do
513
792
 
514
793
  actual_prompt_output = prompt.output.string
515
794
  expected_prompt_output =
516
- output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
795
+ output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", init: true,
796
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
517
797
  output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p"') +
518
798
  output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p*"') +
519
799
  output_helper("What email?", %w(p*2@mail.com), "p*2@mail.com", hint: 'Filter: "p*2"') +
@@ -535,7 +815,8 @@ RSpec.describe TTY::Prompt, '#select' do
535
815
 
536
816
  actual_prompt_output = prompt.output.string
537
817
  expected_prompt_output =
538
- output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
818
+ output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", init: true,
819
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
539
820
  output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
540
821
  output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
541
822
  output_helper("What size?", %w(Large), "Large", hint: 'Filter: "a"') +
@@ -556,7 +837,8 @@ RSpec.describe TTY::Prompt, '#select' do
556
837
  expect(answer).to eql("Small")
557
838
 
558
839
  expected_prompt_output =
559
- output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true, hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
840
+ output_helper("What size?", %w(Small Medium Large Huge), "Small", init: true,
841
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
560
842
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "H"') +
561
843
  output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Hu"') +
562
844
  output_helper("What size?", %w(Small), "Small", hint: 'Filter: "S"') +
@@ -579,7 +861,7 @@ RSpec.describe TTY::Prompt, '#select' do
579
861
 
580
862
  expected_output =
581
863
  output_helper("What size?", choices, "Small", init: true,
582
- hint: "Use arrow keys, press Enter to select") +
864
+ hint: "Use #{up_down} arrow keys, press Enter to select") +
583
865
  output_helper("What size?", choices, "Medium") +
584
866
  output_helper("What size?", choices, "Huge") +
585
867
  "What size? \e[32mHuge\e[0m\n\e[?25h"
@@ -600,7 +882,7 @@ RSpec.describe TTY::Prompt, '#select' do
600
882
 
601
883
  expected_output =
602
884
  output_helper("What letter?", choices, "A", init: true,
603
- hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
885
+ hint: "Use #{up_down} arrow keys, press Enter to select, and letter keys to filter") +
604
886
  output_helper("What letter?", [], "", hint: 'Filter: "c"') +
605
887
  output_helper("What letter?", [], "", hint: 'Filter: "c"') +
606
888
  output_helper("What letter?", ['A'], "A", hint: 'Filter: "a"') +
@@ -625,7 +907,7 @@ RSpec.describe TTY::Prompt, '#select' do
625
907
 
626
908
  expected_output =
627
909
  output_helper("What size?", choices, "Small", init: true, enum: ') ',
628
- hint: "Use arrow or number (1-3) keys, press Enter to select") +
910
+ hint: "Use #{up_down} arrow or number (1-3) keys, press Enter to select") +
629
911
  output_helper("What size?", choices, "Small", enum: ') ') +
630
912
  "What size? \e[32mSmall\e[0m\n\e[?25h"
631
913