tty-prompt 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/tty/prompt/messages.rb +49 -0
  4. data/lib/tty/prompt/version.rb +1 -3
  5. data/spec/spec_helper.rb +45 -0
  6. data/spec/unit/ask_spec.rb +132 -0
  7. data/spec/unit/choice/eql_spec.rb +22 -0
  8. data/spec/unit/choice/from_spec.rb +96 -0
  9. data/spec/unit/choices/add_spec.rb +12 -0
  10. data/spec/unit/choices/each_spec.rb +13 -0
  11. data/spec/unit/choices/find_by_spec.rb +10 -0
  12. data/spec/unit/choices/new_spec.rb +10 -0
  13. data/spec/unit/choices/pluck_spec.rb +9 -0
  14. data/spec/unit/collect_spec.rb +96 -0
  15. data/spec/unit/converters/convert_bool_spec.rb +58 -0
  16. data/spec/unit/converters/convert_char_spec.rb +11 -0
  17. data/spec/unit/converters/convert_custom_spec.rb +14 -0
  18. data/spec/unit/converters/convert_date_spec.rb +34 -0
  19. data/spec/unit/converters/convert_file_spec.rb +18 -0
  20. data/spec/unit/converters/convert_number_spec.rb +39 -0
  21. data/spec/unit/converters/convert_path_spec.rb +15 -0
  22. data/spec/unit/converters/convert_range_spec.rb +22 -0
  23. data/spec/unit/converters/convert_regex_spec.rb +12 -0
  24. data/spec/unit/converters/convert_string_spec.rb +21 -0
  25. data/spec/unit/converters/on_error_spec.rb +9 -0
  26. data/spec/unit/distance/distance_spec.rb +73 -0
  27. data/spec/unit/enum_paginator_spec.rb +75 -0
  28. data/spec/unit/enum_select_spec.rb +446 -0
  29. data/spec/unit/error_spec.rb +20 -0
  30. data/spec/unit/evaluator_spec.rb +67 -0
  31. data/spec/unit/expand_spec.rb +198 -0
  32. data/spec/unit/keypress_spec.rb +72 -0
  33. data/spec/unit/mask_spec.rb +132 -0
  34. data/spec/unit/multi_select_spec.rb +495 -0
  35. data/spec/unit/multiline_spec.rb +77 -0
  36. data/spec/unit/new_spec.rb +20 -0
  37. data/spec/unit/ok_spec.rb +10 -0
  38. data/spec/unit/paginator_spec.rb +73 -0
  39. data/spec/unit/question/checks_spec.rb +97 -0
  40. data/spec/unit/question/default_spec.rb +31 -0
  41. data/spec/unit/question/echo_spec.rb +38 -0
  42. data/spec/unit/question/in_spec.rb +115 -0
  43. data/spec/unit/question/initialize_spec.rb +12 -0
  44. data/spec/unit/question/modifier/apply_to_spec.rb +24 -0
  45. data/spec/unit/question/modifier/letter_case_spec.rb +41 -0
  46. data/spec/unit/question/modifier/whitespace_spec.rb +51 -0
  47. data/spec/unit/question/modify_spec.rb +41 -0
  48. data/spec/unit/question/required_spec.rb +92 -0
  49. data/spec/unit/question/validate_spec.rb +115 -0
  50. data/spec/unit/question/validation/call_spec.rb +31 -0
  51. data/spec/unit/question/validation/coerce_spec.rb +30 -0
  52. data/spec/unit/result_spec.rb +40 -0
  53. data/spec/unit/say_spec.rb +67 -0
  54. data/spec/unit/select_spec.rb +624 -0
  55. data/spec/unit/slider_spec.rb +100 -0
  56. data/spec/unit/statement/initialize_spec.rb +15 -0
  57. data/spec/unit/subscribe_spec.rb +20 -0
  58. data/spec/unit/suggest_spec.rb +28 -0
  59. data/spec/unit/warn_spec.rb +21 -0
  60. data/spec/unit/yes_no_spec.rb +235 -0
  61. data/tty-prompt.gemspec +4 -6
  62. metadata +120 -15
  63. data/.gitignore +0 -16
  64. data/.rspec +0 -3
  65. data/.travis.yml +0 -27
  66. data/CHANGELOG.md +0 -289
  67. data/CODE_OF_CONDUCT.md +0 -49
  68. data/Gemfile +0 -21
  69. data/appveyor.yml +0 -22
  70. data/benchmarks/speed.rb +0 -27
@@ -0,0 +1,495 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Prompt do
4
+ let(:symbols) { TTY::Prompt::Symbols.symbols }
5
+
6
+ def output_helper(prompt, choices, active, selected, options = {})
7
+ raise ":init requires :hint" if options[:init] && options[:hint].nil?
8
+ hint = options[:hint]
9
+ init = options.fetch(:init, false)
10
+ enum = options[:enum]
11
+
12
+ out = ""
13
+ out << "\e[?25l" if init
14
+ out << prompt << " "
15
+ out << selected.join(', ')
16
+ out << "\e[90m" if init
17
+ out << (init ? "(#{hint})\e[0m" : " (#{hint})") if hint
18
+ out << "\n"
19
+ out << choices.map.with_index do |choice, i|
20
+ name = choice.is_a?(Hash) ? choice[:name] : choice
21
+ disabled = choice.is_a?(Hash) ? choice[:disabled] : false
22
+ num = (i + 1).to_s + enum if enum
23
+
24
+ prefix = name == active ? "#{symbols[:pointer]} " : " "
25
+ prefix += if disabled
26
+ "\e[31m#{symbols[:cross]}\e[0m #{num}#{name} #{disabled}"
27
+ elsif selected.include?(name)
28
+ "\e[32m#{symbols[:radio_on]}\e[0m #{num}#{name}"
29
+ else
30
+ "#{symbols[:radio_off]} #{num}#{name}"
31
+ end
32
+ prefix
33
+ end.join("\n")
34
+ out << "\e[2K\e[1G\e[1A" * choices.count
35
+ out << "\e[2K\e[1G"
36
+ out
37
+ end
38
+
39
+ def exit_message(prompt, choices)
40
+ "#{prompt} \e[32m#{choices.join(', ')}\e[0m\n\e[?25h"
41
+ end
42
+
43
+ # Ensure a wide prompt on CI
44
+ before { allow(TTY::Screen).to receive(:width).and_return(200) }
45
+
46
+ it "selects nothing when return pressed immediately" do
47
+ prompt = TTY::TestPrompt.new
48
+ choices = %w(vodka beer wine whisky bourbon)
49
+ prompt.input << "\r"
50
+ prompt.input.rewind
51
+ expect(prompt.multi_select("Select drinks?", choices)). to eq([])
52
+ expect(prompt.output.string).to eq([
53
+ "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
54
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
55
+ " #{symbols[:radio_off]} beer\n",
56
+ " #{symbols[:radio_off]} wine\n",
57
+ " #{symbols[:radio_off]} whisky\n",
58
+ " #{symbols[:radio_off]} bourbon",
59
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
60
+ "Select drinks? \n\e[?25h"
61
+ ].join)
62
+ end
63
+
64
+ it "selects item when space pressed" do
65
+ prompt = TTY::TestPrompt.new
66
+ choices = %w(vodka beer wine whisky bourbon)
67
+ prompt.input << " \r"
68
+ prompt.input.rewind
69
+ expect(prompt.multi_select("Select drinks?", choices)). to eq(['vodka'])
70
+ expect(prompt.output.string).to eq([
71
+ "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
72
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
73
+ " #{symbols[:radio_off]} beer\n",
74
+ " #{symbols[:radio_off]} wine\n",
75
+ " #{symbols[:radio_off]} whisky\n",
76
+ " #{symbols[:radio_off]} bourbon",
77
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
78
+ "Select drinks? vodka\n",
79
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n",
80
+ " #{symbols[:radio_off]} beer\n",
81
+ " #{symbols[:radio_off]} wine\n",
82
+ " #{symbols[:radio_off]} whisky\n",
83
+ " #{symbols[:radio_off]} bourbon",
84
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
85
+ "Select drinks? \e[32mvodka\e[0m\n\e[?25h"
86
+ ].join)
87
+ end
88
+
89
+ it "selects item when space pressed but doesn't echo item if echo: false" do
90
+ prompt = TTY::TestPrompt.new
91
+ choices = %w(vodka beer wine whisky bourbon)
92
+ prompt.input << " \r"
93
+ prompt.input.rewind
94
+ expect(prompt.multi_select("Select drinks?", choices, echo: false)). to eq(['vodka'])
95
+ expect(prompt.output.string).to eq([
96
+ "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
97
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
98
+ " #{symbols[:radio_off]} beer\n",
99
+ " #{symbols[:radio_off]} wine\n",
100
+ " #{symbols[:radio_off]} whisky\n",
101
+ " #{symbols[:radio_off]} bourbon",
102
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
103
+ "Select drinks? \n",
104
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n",
105
+ " #{symbols[:radio_off]} beer\n",
106
+ " #{symbols[:radio_off]} wine\n",
107
+ " #{symbols[:radio_off]} whisky\n",
108
+ " #{symbols[:radio_off]} bourbon",
109
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
110
+ "Select drinks? \n\e[?25h"
111
+ ].join)
112
+ end
113
+
114
+ it "sets choice custom values" do
115
+ prompt = TTY::TestPrompt.new
116
+ choices = {vodka: 1, beer: 2, wine: 3, whisky: 4, bourbon: 5}
117
+ prompt.input << " \r"
118
+ prompt.input.rewind
119
+ expect(prompt.multi_select("Select drinks?", choices)).to eq([1])
120
+ expect(prompt.output.string).to eq([
121
+ "\e[?25lSelect drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
122
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
123
+ " #{symbols[:radio_off]} beer\n",
124
+ " #{symbols[:radio_off]} wine\n",
125
+ " #{symbols[:radio_off]} whisky\n",
126
+ " #{symbols[:radio_off]} bourbon",
127
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
128
+ "Select drinks? vodka\n",
129
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m vodka\n",
130
+ " #{symbols[:radio_off]} beer\n",
131
+ " #{symbols[:radio_off]} wine\n",
132
+ " #{symbols[:radio_off]} whisky\n",
133
+ " #{symbols[:radio_off]} bourbon",
134
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
135
+ "Select drinks? \e[32mvodka\e[0m\n\e[?25h"
136
+ ].join)
137
+ end
138
+
139
+ it "sets choice name and value through DSL" do
140
+ prompt = TTY::TestPrompt.new
141
+ prompt.input << " \r"
142
+ prompt.input.rewind
143
+ value = prompt.multi_select("Select drinks?") do |menu|
144
+ menu.enum ')'
145
+
146
+ menu.choice :vodka, {score: 1}
147
+ menu.choice :beer, 2
148
+ menu.choice :wine, 3
149
+ menu.choices whisky: 4, bourbon: 5
150
+ end
151
+ expect(value).to eq([{score: 1}])
152
+ expect(prompt.output.string).to eq([
153
+ "\e[?25lSelect drinks? \e[90m(Use arrow or number (1-5) keys, press Space to select and Enter to finish)\e[0m\n",
154
+ "#{symbols[:pointer]} #{symbols[:radio_off]} 1) vodka\n",
155
+ " #{symbols[:radio_off]} 2) beer\n",
156
+ " #{symbols[:radio_off]} 3) wine\n",
157
+ " #{symbols[:radio_off]} 4) whisky\n",
158
+ " #{symbols[:radio_off]} 5) bourbon",
159
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
160
+ "Select drinks? vodka\n",
161
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m 1) vodka\n",
162
+ " #{symbols[:radio_off]} 2) beer\n",
163
+ " #{symbols[:radio_off]} 3) wine\n",
164
+ " #{symbols[:radio_off]} 4) whisky\n",
165
+ " #{symbols[:radio_off]} 5) bourbon",
166
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
167
+ "Select drinks? \e[32mvodka\e[0m\n\e[?25h"
168
+ ].join)
169
+ end
170
+
171
+ it "sets default options through DSL syntax" do
172
+ prompt = TTY::TestPrompt.new
173
+ prompt.input << "\r"
174
+ prompt.input.rewind
175
+ value = prompt.multi_select("Select drinks?") do |menu|
176
+ menu.default 2, 5
177
+
178
+ menu.choice :vodka, {score: 10}
179
+ menu.choice :beer, {score: 20}
180
+ menu.choice :wine, {score: 30}
181
+ menu.choice :whisky, {score: 40}
182
+ menu.choice :bourbon, {score: 50}
183
+ end
184
+ expect(value).to match_array([{score: 20}, {score: 50}])
185
+ expect(prompt.output.string).to eq([
186
+ "\e[?25lSelect drinks? beer, bourbon \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
187
+ " #{symbols[:radio_off]} vodka\n",
188
+ " \e[32m#{symbols[:radio_on]}\e[0m beer\n",
189
+ " #{symbols[:radio_off]} wine\n",
190
+ " #{symbols[:radio_off]} whisky\n",
191
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m bourbon",
192
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
193
+ "Select drinks? \e[32mbeer, bourbon\e[0m\n\e[?25h",
194
+ ].join)
195
+ end
196
+
197
+ it "sets default options through hash syntax" do
198
+ prompt = TTY::TestPrompt.new
199
+ prompt.input << "\r"
200
+ prompt.input.rewind
201
+ value = prompt.multi_select("Select drinks?", default: [2, 5]) do |menu|
202
+ menu.choice :vodka, {score: 10}
203
+ menu.choice :beer, {score: 20}
204
+ menu.choice :wine, {score: 30}
205
+ menu.choice :whisky, {score: 40}
206
+ menu.choice :bourbon, {score: 50}
207
+ end
208
+ expect(value).to match_array([{score: 20}, {score: 50}])
209
+ end
210
+
211
+ it "raises error for defaults out of range" do
212
+ prompt = TTY::TestPrompt.new
213
+ prompt.input << "\r"
214
+ prompt.input.rewind
215
+ expect {
216
+ prompt.multi_select("Select drinks?", default: [2, 6]) do |menu|
217
+ menu.choice :vodka, {score: 10}
218
+ menu.choice :beer, {score: 20}
219
+ menu.choice :wine, {score: 30}
220
+ menu.choice :whisky, {score: 40}
221
+ menu.choice :bourbon, {score: 50}
222
+ end
223
+ }.to raise_error(TTY::Prompt::ConfigurationError,
224
+ /default index `6` out of range \(1 - 5\)/)
225
+ end
226
+
227
+ it "sets prompt prefix" do
228
+ prompt = TTY::TestPrompt.new(prefix: '[?] ')
229
+ choices = %w(vodka beer wine whisky bourbon)
230
+ prompt.input << "\r"
231
+ prompt.input.rewind
232
+ expect(prompt.multi_select("Select drinks?", choices)). to eq([])
233
+ expect(prompt.output.string).to eq([
234
+ "\e[?25l[?] Select drinks? \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
235
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
236
+ " #{symbols[:radio_off]} beer\n",
237
+ " #{symbols[:radio_off]} wine\n",
238
+ " #{symbols[:radio_off]} whisky\n",
239
+ " #{symbols[:radio_off]} bourbon",
240
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
241
+ "[?] Select drinks? \n\e[?25h"
242
+ ].join)
243
+ end
244
+
245
+ it "changes selected item color & marker" do
246
+ prompt = TTY::TestPrompt.new
247
+ choices = %w(vodka beer wine whisky bourbon)
248
+ prompt.input << "\r"
249
+ prompt.input.rewind
250
+ options = {default: [1], active_color: :blue, marker: '>'}
251
+ expect(prompt.multi_select("Select drinks?", choices, options)). to eq(['vodka'])
252
+ expect(prompt.output.string).to eq([
253
+ "\e[?25lSelect drinks? vodka \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
254
+ "> \e[34m#{symbols[:radio_on]}\e[0m vodka\n",
255
+ " #{symbols[:radio_off]} beer\n",
256
+ " #{symbols[:radio_off]} wine\n",
257
+ " #{symbols[:radio_off]} whisky\n",
258
+ " #{symbols[:radio_off]} bourbon",
259
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
260
+ "Select drinks? \e[34mvodka\e[0m\n\e[?25h"
261
+ ].join)
262
+ end
263
+
264
+ it "changes help text" do
265
+ prompt = TTY::TestPrompt.new
266
+ choices = %w(vodka beer wine whisky bourbon)
267
+ prompt.input << "\r"
268
+ prompt.input.rewind
269
+ expect(prompt.multi_select("Select drinks?", choices, help: '(Bash keyboard)')). to eq([])
270
+ expect(prompt.output.string).to eq([
271
+ "\e[?25lSelect drinks? \e[90m(Bash keyboard)\e[0m\n",
272
+ "#{symbols[:pointer]} #{symbols[:radio_off]} vodka\n",
273
+ " #{symbols[:radio_off]} beer\n",
274
+ " #{symbols[:radio_off]} wine\n",
275
+ " #{symbols[:radio_off]} whisky\n",
276
+ " #{symbols[:radio_off]} bourbon",
277
+ "\e[2K\e[1G\e[1A" * 5, "\e[2K\e[1G",
278
+ "Select drinks? \n\e[?25h"
279
+ ].join)
280
+ end
281
+
282
+ it "paginates long selections" do
283
+ prompt = TTY::TestPrompt.new
284
+ choices = %w(A B C D E F G H)
285
+ prompt.input << "\r"
286
+ prompt.input.rewind
287
+ value = prompt.multi_select("What letter?", choices, per_page: 3, default: 4)
288
+ expect(value).to eq(['D'])
289
+ expect(prompt.output.string).to eq([
290
+ "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
291
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n",
292
+ " #{symbols[:radio_off]} E\n",
293
+ " #{symbols[:radio_off]} F\n",
294
+ "\e[90m(Move up or down to reveal more choices)\e[0m",
295
+ "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G",
296
+ "What letter? \e[32mD\e[0m\n\e[?25h",
297
+ ].join)
298
+ end
299
+
300
+ it "paginates choices as hash object" do
301
+ prompt = TTY::TestPrompt.new
302
+ choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
303
+ prompt.input << "\r"
304
+ prompt.input.rewind
305
+ value = prompt.multi_select("What letter?", choices, default: 4, per_page: 3)
306
+ expect(value).to eq([4])
307
+ expect(prompt.output.string).to eq([
308
+ "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
309
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n",
310
+ " #{symbols[:radio_off]} E\n",
311
+ " #{symbols[:radio_off]} F\n",
312
+ "\e[90m(Move up or down to reveal more choices)\e[0m",
313
+ "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G",
314
+ "What letter? \e[32mD\e[0m\n\e[?25h",
315
+ ].join)
316
+ end
317
+
318
+ it "paginates long selections through DSL" do
319
+ prompt = TTY::TestPrompt.new
320
+ choices = %w(A B C D E F G H)
321
+ prompt.input << "\r"
322
+ prompt.input.rewind
323
+ value = prompt.multi_select("What letter?") do |menu|
324
+ menu.per_page 3
325
+ menu.page_help '(Wiggle thy finger up or down to see more)'
326
+ menu.default 4
327
+ menu.choices choices
328
+ end
329
+ expect(value).to eq(['D'])
330
+ expect(prompt.output.string).to eq([
331
+ "\e[?25lWhat letter? D \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
332
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m D\n",
333
+ " #{symbols[:radio_off]} E\n",
334
+ " #{symbols[:radio_off]} F\n",
335
+ "\e[90m(Wiggle thy finger up or down to see more)\e[0m",
336
+ "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G",
337
+ "What letter? \e[32mD\e[0m\n\e[?25h",
338
+ ].join)
339
+ end
340
+
341
+ it "doesn't paginate short selections" do
342
+ prompt = TTY::TestPrompt.new
343
+ choices = %w(A B C D)
344
+ prompt.input << "\r"
345
+ prompt.input.rewind
346
+ value = prompt.multi_select("What letter?", choices, per_page: 4, default: 1)
347
+ expect(value).to eq(['A'])
348
+
349
+ expect(prompt.output.string).to eq([
350
+ "\e[?25lWhat letter? A \e[90m(Use arrow keys, press Space to select and Enter to finish)\e[0m\n",
351
+ "#{symbols[:pointer]} \e[32m#{symbols[:radio_on]}\e[0m A\n",
352
+ " #{symbols[:radio_off]} B\n",
353
+ " #{symbols[:radio_off]} C\n",
354
+ " #{symbols[:radio_off]} D",
355
+ "\e[2K\e[1G\e[1A" * 4, "\e[2K\e[1G",
356
+ "What letter? \e[32mA\e[0m\n\e[?25h",
357
+ ].join)
358
+ end
359
+
360
+ context "with :cycle" do
361
+ it "doesn't cycle by default" do
362
+ prompt = TTY::TestPrompt.new
363
+ choices = %w(A B C)
364
+ prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
365
+ prompt.input << "j" << "j" << "j" << " " << "\r"
366
+ prompt.input.rewind
367
+
368
+ value = prompt.multi_select("What letter?", choices)
369
+ expect(value).to eq(["C"])
370
+
371
+ expect(prompt.output.string).to eq(
372
+ output_helper("What letter?", choices, "A", [], init: true,
373
+ hint: "Use arrow keys, press Space to select and Enter to finish") +
374
+ output_helper("What letter?", choices, "B", []) +
375
+ output_helper("What letter?", choices, "C", []) +
376
+ output_helper("What letter?", choices, "C", []) +
377
+ output_helper("What letter?", choices, "C", ["C"]) +
378
+ "What letter? \e[32mC\e[0m\n\e[?25h"
379
+ )
380
+ end
381
+
382
+ it "cycles when configured to do so" do
383
+ prompt = TTY::TestPrompt.new
384
+ choices = %w(A B C)
385
+ prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
386
+ prompt.input << "j" << "j" << "j" << " " << "\r"
387
+ prompt.input.rewind
388
+ value = prompt.multi_select("What letter?", choices, cycle: true)
389
+ expect(value).to eq(["A"])
390
+ expect(prompt.output.string).to eq(
391
+ output_helper("What letter?", choices, "A", [], init: true,
392
+ hint: "Use arrow keys, press Space to select and Enter to finish") +
393
+ output_helper("What letter?", choices, "B", []) +
394
+ output_helper("What letter?", choices, "C", []) +
395
+ output_helper("What letter?", choices, "A", []) +
396
+ output_helper("What letter?", choices, "A", ["A"]) +
397
+ "What letter? \e[32mA\e[0m\n\e[?25h"
398
+ )
399
+ end
400
+ end
401
+
402
+ context "with filter" do
403
+ it "doesn't lose the selection when switching between filters" do
404
+ prompt = TTY::TestPrompt.new
405
+ prompt.on(:keypress) { |e| prompt.trigger(:keydelete) if e.value == "\r" }
406
+ prompt.input << " " # select `Tiny`
407
+ prompt.input << "a" << " " # match and select `Large`
408
+ prompt.input << "\u007F" # backspace (shows all)
409
+ prompt.input << "\r"
410
+ prompt.input.rewind
411
+
412
+ answer = prompt.multi_select("What size?", %w(Tiny Medium Large Huge), filter: true)
413
+ expect(answer).to eql(%w(Tiny Large))
414
+
415
+ expected_prompt_output =
416
+ output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(), init: true, hint: "Use arrow keys, press Space to select and Enter to finish, and letter keys to filter") +
417
+ output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(Tiny)) +
418
+ output_helper("What size?", %w(Large), "Large", %w(Tiny), hint: 'Filter: "a"') +
419
+ output_helper("What size?", %w(Large), "Large", %w(Tiny Large), hint: 'Filter: "a"') +
420
+ output_helper("What size?", %w(Tiny Medium Large Huge), "Tiny", %w(Tiny Large)) +
421
+ exit_message("What size?", %w(Tiny Large))
422
+
423
+ expect(prompt.output.string).to eql(expected_prompt_output)
424
+ end
425
+ end
426
+
427
+ context "with :disabled" do
428
+ it "fails when active item is also disabled" do
429
+ prompt = TTY::TestPrompt.new
430
+ choices = [{name: 'vodka', disabled: true}, 'beer', 'wine', 'whisky', 'bourbon']
431
+ expect {
432
+ prompt.multi_select("Select drinks?", choices)
433
+ }.to raise_error(TTY::Prompt::ConfigurationError,
434
+ /active choice 'vodka' matches disabled item/)
435
+ end
436
+
437
+ it "omits disabled choice when nagivating menu" do
438
+ choices = [
439
+ {name: 'A'},
440
+ {name: 'B', disabled: '(out)'},
441
+ {name: 'C', disabled: '(out)'},
442
+ {name: 'D'},
443
+ {name: 'E'}
444
+ ]
445
+ prompt = TTY::TestPrompt.new
446
+ prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
447
+ prompt.input << "j" << " " << "j" << " " << "\r"
448
+ prompt.input.rewind
449
+
450
+ answer = prompt.multi_select("What letter?", choices)
451
+ expect(answer).to eq(%w[D E])
452
+
453
+ expected_output =
454
+ output_helper("What letter?", choices, "A", [], init: true, hint: "Use arrow keys, press Space to select and Enter to finish") +
455
+ output_helper("What letter?", choices, "D", []) +
456
+ output_helper("What letter?", choices, "D", %w[D]) +
457
+ output_helper("What letter?", choices, "E", %w[D]) +
458
+ output_helper("What letter?", choices, "E", %w[D E]) +
459
+ exit_message("What letter?", %w[D E])
460
+
461
+ expect(prompt.output.string).to eq(expected_output)
462
+ end
463
+
464
+ it "omits disabled choice when number key is pressed" do
465
+ choices = [
466
+ {name: 'vodka', value: 1},
467
+ {name: 'beer', value: 1, disabled: true},
468
+ {name: 'wine', value: 1},
469
+ {name: 'whisky', value: 1, disabled: true},
470
+ {name: 'bourbon', value: 1}
471
+ ]
472
+ prompt = TTY::TestPrompt.new
473
+ prompt.input << "2" << " \r"
474
+ prompt.input.rewind
475
+ answer = prompt.multi_select("Select drinks?") do |menu|
476
+ menu.enum ')'
477
+
478
+ menu.choice :vodka, 1
479
+ menu.choice :beer, 2, disabled: true
480
+ menu.choice :wine, 3
481
+ menu.choice :whisky, 4, disabled: true
482
+ menu.choice :bourbon, 5
483
+ end
484
+ expect(answer).to eq([1])
485
+
486
+ expected_output =
487
+ output_helper("Select drinks?", choices, "vodka", [], init: true, enum: ') ', hint: "Use arrow or number (1-5) keys, press Space to select and Enter to finish") +
488
+ output_helper("Select drinks?", choices, "vodka", [], enum: ') ') +
489
+ output_helper("Select drinks?", choices, "vodka", %w[vodka], enum: ') ') +
490
+ exit_message("Select drinks?", %w[vodka])
491
+
492
+ expect(prompt.output.string).to eq(expected_output)
493
+ end
494
+ end
495
+ end