tty-prompt 0.18.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -0
  3. data/README.md +549 -248
  4. data/lib/tty-prompt.rb +1 -2
  5. data/lib/tty/prompt.rb +187 -143
  6. data/lib/tty/prompt/answers_collector.rb +5 -5
  7. data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
  8. data/lib/tty/prompt/choice.rb +5 -7
  9. data/lib/tty/prompt/choices.rb +29 -11
  10. data/lib/tty/prompt/confirm_question.rb +38 -16
  11. data/lib/tty/prompt/const.rb +17 -0
  12. data/lib/tty/prompt/converter_dsl.rb +6 -7
  13. data/lib/tty/prompt/converter_registry.rb +31 -26
  14. data/lib/tty/prompt/converters.rb +139 -32
  15. data/lib/tty/prompt/enum_list.rb +57 -27
  16. data/lib/tty/prompt/errors.rb +31 -0
  17. data/lib/tty/prompt/evaluator.rb +1 -1
  18. data/lib/tty/prompt/expander.rb +39 -13
  19. data/lib/tty/prompt/keypress.rb +31 -36
  20. data/lib/tty/prompt/list.rb +175 -65
  21. data/lib/tty/prompt/mask_question.rb +4 -5
  22. data/lib/tty/prompt/multi_list.rb +124 -33
  23. data/lib/tty/prompt/multiline.rb +7 -6
  24. data/lib/tty/prompt/paginator.rb +38 -26
  25. data/lib/tty/prompt/question.rb +83 -34
  26. data/lib/tty/prompt/question/checks.rb +18 -0
  27. data/lib/tty/prompt/question/validation.rb +3 -3
  28. data/lib/tty/prompt/selected_choices.rb +76 -0
  29. data/lib/tty/prompt/slider.rb +83 -16
  30. data/lib/tty/prompt/statement.rb +3 -3
  31. data/lib/tty/prompt/suggestion.rb +6 -6
  32. data/lib/tty/prompt/symbols.rb +58 -34
  33. data/lib/tty/prompt/test.rb +36 -0
  34. data/lib/tty/prompt/timer.rb +75 -0
  35. data/lib/tty/prompt/utils.rb +1 -3
  36. data/lib/tty/prompt/version.rb +1 -1
  37. metadata +29 -227
  38. data/Rakefile +0 -8
  39. data/examples/ask.rb +0 -7
  40. data/examples/ask_valid.rb +0 -12
  41. data/examples/collect.rb +0 -21
  42. data/examples/echo.rb +0 -11
  43. data/examples/enum_select.rb +0 -7
  44. data/examples/enum_select_disabled.rb +0 -16
  45. data/examples/enum_select_paged.rb +0 -9
  46. data/examples/enum_select_wrapped.rb +0 -15
  47. data/examples/expand.rb +0 -29
  48. data/examples/in.rb +0 -9
  49. data/examples/inputs.rb +0 -10
  50. data/examples/key_events.rb +0 -15
  51. data/examples/keypress.rb +0 -9
  52. data/examples/mask.rb +0 -13
  53. data/examples/multi_select.rb +0 -8
  54. data/examples/multi_select_disabled.rb +0 -17
  55. data/examples/multi_select_paged.rb +0 -9
  56. data/examples/multi_select_wrapped.rb +0 -15
  57. data/examples/multiline.rb +0 -9
  58. data/examples/pause.rb +0 -9
  59. data/examples/select.rb +0 -20
  60. data/examples/select_disabled.rb +0 -18
  61. data/examples/select_enum.rb +0 -8
  62. data/examples/select_filtered.rb +0 -11
  63. data/examples/select_paginated.rb +0 -11
  64. data/examples/select_wrapped.rb +0 -15
  65. data/examples/slider.rb +0 -6
  66. data/examples/validation.rb +0 -9
  67. data/examples/yes_no.rb +0 -7
  68. data/lib/tty/prompt/messages.rb +0 -49
  69. data/lib/tty/prompt/timeout.rb +0 -78
  70. data/lib/tty/test_prompt.rb +0 -20
  71. data/spec/spec_helper.rb +0 -45
  72. data/spec/unit/ask_spec.rb +0 -132
  73. data/spec/unit/choice/eql_spec.rb +0 -22
  74. data/spec/unit/choice/from_spec.rb +0 -96
  75. data/spec/unit/choices/add_spec.rb +0 -12
  76. data/spec/unit/choices/each_spec.rb +0 -13
  77. data/spec/unit/choices/find_by_spec.rb +0 -10
  78. data/spec/unit/choices/new_spec.rb +0 -10
  79. data/spec/unit/choices/pluck_spec.rb +0 -9
  80. data/spec/unit/collect_spec.rb +0 -96
  81. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  82. data/spec/unit/converters/convert_char_spec.rb +0 -11
  83. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  84. data/spec/unit/converters/convert_date_spec.rb +0 -34
  85. data/spec/unit/converters/convert_file_spec.rb +0 -18
  86. data/spec/unit/converters/convert_number_spec.rb +0 -39
  87. data/spec/unit/converters/convert_path_spec.rb +0 -15
  88. data/spec/unit/converters/convert_range_spec.rb +0 -22
  89. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  90. data/spec/unit/converters/convert_string_spec.rb +0 -21
  91. data/spec/unit/converters/on_error_spec.rb +0 -9
  92. data/spec/unit/distance/distance_spec.rb +0 -73
  93. data/spec/unit/enum_paginator_spec.rb +0 -75
  94. data/spec/unit/enum_select_spec.rb +0 -446
  95. data/spec/unit/error_spec.rb +0 -20
  96. data/spec/unit/evaluator_spec.rb +0 -67
  97. data/spec/unit/expand_spec.rb +0 -198
  98. data/spec/unit/keypress_spec.rb +0 -72
  99. data/spec/unit/mask_spec.rb +0 -132
  100. data/spec/unit/multi_select_spec.rb +0 -495
  101. data/spec/unit/multiline_spec.rb +0 -77
  102. data/spec/unit/new_spec.rb +0 -20
  103. data/spec/unit/ok_spec.rb +0 -10
  104. data/spec/unit/paginator_spec.rb +0 -73
  105. data/spec/unit/question/checks_spec.rb +0 -97
  106. data/spec/unit/question/default_spec.rb +0 -31
  107. data/spec/unit/question/echo_spec.rb +0 -38
  108. data/spec/unit/question/in_spec.rb +0 -115
  109. data/spec/unit/question/initialize_spec.rb +0 -12
  110. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  111. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  112. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  113. data/spec/unit/question/modify_spec.rb +0 -41
  114. data/spec/unit/question/required_spec.rb +0 -92
  115. data/spec/unit/question/validate_spec.rb +0 -115
  116. data/spec/unit/question/validation/call_spec.rb +0 -31
  117. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  118. data/spec/unit/result_spec.rb +0 -40
  119. data/spec/unit/say_spec.rb +0 -67
  120. data/spec/unit/select_spec.rb +0 -643
  121. data/spec/unit/slider_spec.rb +0 -100
  122. data/spec/unit/statement/initialize_spec.rb +0 -15
  123. data/spec/unit/subscribe_spec.rb +0 -22
  124. data/spec/unit/suggest_spec.rb +0 -28
  125. data/spec/unit/warn_spec.rb +0 -21
  126. data/spec/unit/yes_no_spec.rb +0 -251
  127. data/tasks/console.rake +0 -11
  128. data/tasks/coverage.rake +0 -11
  129. data/tasks/spec.rake +0 -29
  130. data/tty-prompt.gemspec +0 -33
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt::Result do
4
- it "checks value to be invalid" do
5
- question = double(:question)
6
- result = TTY::Prompt::Result.new(question, nil)
7
-
8
- answer = result.with { |quest, value|
9
- if value.nil?
10
- [value, ["`#{value}` provided cannot be empty"]]
11
- else
12
- value
13
- end
14
- }
15
- expect(answer).to be_a(TTY::Prompt::Result::Failure)
16
- expect(answer.success?).to eq(false)
17
- expect(answer.errors).to eq(["`` provided cannot be empty"])
18
- end
19
-
20
- it "checks value to be valid" do
21
- question = double(:question)
22
- result = TTY::Prompt::Result.new(question, 'Piotr')
23
-
24
- CheckRequired = Class.new do
25
- def self.call(quest, value)
26
- if value.nil?
27
- [value, ["`#{value}` provided cannot be empty"]]
28
- else
29
- value
30
- end
31
- end
32
- end
33
-
34
- answer = result.with(CheckRequired)
35
- expect(answer).to be_a(TTY::Prompt::Result::Success)
36
- expect(answer.success?).to eq(true)
37
- expect(answer.value).to eq('Piotr')
38
- expect(answer.errors).to eq([])
39
- end
40
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt, '#say' do
4
-
5
- subject(:prompt) { TTY::TestPrompt.new }
6
-
7
- it 'prints an empty message' do
8
- prompt.say('')
9
- expect(prompt.output.string).to eq('')
10
- end
11
-
12
- context 'with new line' do
13
- it 'prints a message with newline' do
14
- prompt.say("Hell yeah!\n")
15
- expect(prompt.output.string).to eq("Hell yeah!\n")
16
- end
17
-
18
- it 'prints a message with implicit newline' do
19
- prompt.say("Hell yeah!\n")
20
- expect(prompt.output.string).to eq("Hell yeah!\n")
21
- end
22
-
23
- it 'prints a message with newline within text' do
24
- prompt.say("Hell\n yeah!")
25
- expect(prompt.output.string).to eq("Hell\n yeah!\n")
26
- end
27
-
28
- it 'prints a message with newline within text and blank space' do
29
- prompt.say("Hell\n yeah! ")
30
- expect(prompt.output.string).to eq("Hell\n yeah! ")
31
- end
32
-
33
- it 'prints a message without newline' do
34
- prompt.say("Hell yeah!", newline: false)
35
- expect(prompt.output.string).to eq("Hell yeah!")
36
- end
37
- end
38
-
39
- context 'with tab or space' do
40
- it 'prints ' do
41
- prompt.say("Hell yeah!\t")
42
- expect(prompt.output.string).to eq("Hell yeah!\t")
43
- end
44
- end
45
-
46
- context 'with color' do
47
- it 'prints message with ansi color' do
48
- prompt.say('Hell yeah!', color: :green)
49
- expect(prompt.output.string).to eq("\e[32mHell yeah!\e[0m\n")
50
- end
51
-
52
- it 'prints message with ansi color without newline' do
53
- prompt.say('Hell yeah! ', color: :green)
54
- expect(prompt.output.string).to eq("\e[32mHell yeah! \e[0m")
55
- end
56
- end
57
-
58
- context 'without color' do
59
- it 'prints message without ansi' do
60
- prompt = TTY::TestPrompt.new(enable_color: false)
61
-
62
- prompt.say('Hell yeah!', color: :green)
63
-
64
- expect(prompt.output.string).to eq("Hell yeah!\n")
65
- end
66
- end
67
- end
@@ -1,643 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe TTY::Prompt, '#select' do
4
-
5
- subject(:prompt) { TTY::TestPrompt.new }
6
-
7
- let(:symbols) { TTY::Prompt::Symbols.symbols }
8
-
9
- def output_helper(prompt, choices, active, options = {})
10
- raise ":init requires :hint" if options[:init] && options[:hint].nil?
11
- hint = options[:hint]
12
- init = options.fetch(:init, false)
13
- enum = options[:enum]
14
-
15
- out = []
16
- out << "\e[?25l" if init
17
- out << prompt << " "
18
- out << "\e[90m(#{hint})\e[0m" if hint
19
- out << "\n"
20
- out << choices.map.with_index do |c, i|
21
- name = c.is_a?(Hash) ? c[:name] : c
22
- disabled = c.is_a?(Hash) ? c[:disabled] : false
23
- num = (i + 1).to_s + enum if enum
24
- if disabled
25
- "\e[31m#{symbols[:cross]}\e[0m #{num}#{name} #{disabled}"
26
- elsif name == active
27
- "\e[32m#{symbols[:pointer]} #{num}#{name}\e[0m"
28
- else
29
- " #{num}#{name}"
30
- end
31
- end.join("\n")
32
- out << "\e[2K\e[1G\e[1A" * choices.count
33
- out << "\e[2K\e[1G"
34
- out << "\e[1A\e[2K\e[1G" if choices.empty?
35
- out.join
36
- end
37
-
38
- def exit_message(prompt, choice)
39
- "#{prompt} \e[32m#{choice}\e[0m\n\e[?25h"
40
- end
41
-
42
- # Ensure a wide prompt on CI
43
- before { allow(TTY::Screen).to receive(:width).and_return(200) }
44
-
45
- it "selects by default first option" do
46
- choices = %w(Large Medium Small)
47
- prompt.input << "\r"
48
- prompt.input.rewind
49
- expect(prompt.select('What size?', choices)).to eq('Large')
50
- expect(prompt.output.string).to eq([
51
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
52
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
53
- " Medium\n",
54
- " Small",
55
- "\e[2K\e[1G\e[1A" * 3,
56
- "\e[2K\e[1G",
57
- "What size? \e[32mLarge\e[0m\n\e[?25h"
58
- ].join)
59
- end
60
-
61
- it "allows navigation using events without errors" do
62
- choices = %w(Large Medium Small)
63
- prompt.input << "j" << "\r"
64
- prompt.input.rewind
65
- prompt.on(:keypress) do |event|
66
- prompt.trigger(:keydown) if event.value == "j"
67
- end
68
- expect { prompt.select('What size?', choices) }.not_to output.to_stderr
69
- expect(prompt.output.string).to eq([
70
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
71
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
72
- " Medium\n",
73
- " Small",
74
- "\e[2K\e[1G\e[1A" * 3,
75
- "\e[2K\e[1G",
76
- "What size? \n",
77
- " Large\n",
78
- "\e[32m#{symbols[:pointer]} Medium\e[0m\n",
79
- " Small",
80
- "\e[2K\e[1G\e[1A" * 3,
81
- "\e[2K\e[1G",
82
- "What size? \e[32mMedium\e[0m\n\e[?25h"
83
- ].join)
84
- end
85
-
86
- it "sets choice name and value" do
87
- choices = {large: 1, medium: 2, small: 3}
88
- prompt.input << " "
89
- prompt.input.rewind
90
- expect(prompt.select('What size?', choices, default: 1)).to eq(1)
91
- expect(prompt.output.string).to eq([
92
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
93
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
94
- " medium\n",
95
- " small",
96
- "\e[2K\e[1G\e[1A" * 3,
97
- "\e[2K\e[1G",
98
- "What size? \e[32mlarge\e[0m\n\e[?25h"
99
- ].join)
100
- end
101
-
102
- it "sets choice name through DSL" do
103
- prompt.input << " "
104
- prompt.input.rewind
105
- value = prompt.select('What size?') do |menu|
106
- menu.choice "Large"
107
- menu.choice "Medium"
108
- menu.choice "Small"
109
- end
110
- expect(value).to eq('Large')
111
- expect(prompt.output.string).to eq([
112
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
113
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
114
- " Medium\n",
115
- " Small",
116
- "\e[2K\e[1G\e[1A" * 3,
117
- "\e[2K\e[1G",
118
- "What size? \e[32mLarge\e[0m\n\e[?25h"
119
- ].join)
120
- end
121
-
122
- it "sets choice name & value through DSL" do
123
- prompt.input << " "
124
- prompt.input.rewind
125
- value = prompt.select('What size?') do |menu|
126
- menu.choice :large, 1
127
- menu.choice :medium, 2
128
- menu.choice :small, 3
129
- end
130
- expect(value).to eq(1)
131
- expect(prompt.output.string).to eq([
132
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
133
- "\e[32m#{symbols[:pointer]} large\e[0m\n",
134
- " medium\n",
135
- " small",
136
- "\e[2K\e[1G\e[1A" * 3,
137
- "\e[2K\e[1G",
138
- "What size? \e[32mlarge\e[0m\n\e[?25h"
139
- ].join)
140
- end
141
-
142
- it "sets choices and single choice through DSL" do
143
- prompt.input << " "
144
- prompt.input.rewind
145
- value = prompt.select('What size?') do |menu|
146
- menu.choice 'Large'
147
- menu.choices %w(Medium Small)
148
- end
149
- expect(value).to eq('Large')
150
- expect(prompt.output.string).to eq([
151
- "\e[?25lWhat size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
152
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
153
- " Medium\n",
154
- " Small",
155
- "\e[2K\e[1G\e[1A" * 3,
156
- "\e[2K\e[1G",
157
- "What size? \e[32mLarge\e[0m\n\e[?25h"
158
- ].join)
159
- end
160
-
161
- it "sets choice name & value through DSL" do
162
- prompt.input << " "
163
- prompt.input.rewind
164
- value = prompt.select('What size?') do |menu|
165
- menu.default 2
166
- menu.enum '.'
167
-
168
- menu.choice :large, 1
169
- menu.choice :medium, 2
170
- menu.choice :small, 3
171
- end
172
- expect(value).to eq(2)
173
- expect(prompt.output.string).to eq([
174
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
175
- " 1. large\n",
176
- "\e[32m#{symbols[:pointer]} 2. medium\e[0m\n",
177
- " 3. small",
178
- "\e[2K\e[1G\e[1A" * 3,
179
- "\e[2K\e[1G",
180
- "What size? \e[32mmedium\e[0m\n\e[?25h"
181
- ].join)
182
- end
183
-
184
- it "sets choice value to proc and executes it" do
185
- prompt.input << " "
186
- prompt.input.rewind
187
- value = prompt.select('What size?', default: 2, enum: ')') do |menu|
188
- menu.choice :large, 1
189
- menu.choice :medium do 'Good choice!' end
190
- menu.choice :small, 3
191
- end
192
- expect(value).to eq('Good choice!')
193
- expect(prompt.output.string).to eq([
194
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
195
- " 1) large\n",
196
- "\e[32m#{symbols[:pointer]} 2) medium\e[0m\n",
197
- " 3) small",
198
- "\e[2K\e[1G\e[1A" * 3,
199
- "\e[2K\e[1G",
200
- "What size? \e[32mmedium\e[0m\n\e[?25h"
201
- ].join)
202
- end
203
-
204
- it "sets default option through hash syntax" do
205
- choices = %w(Large Medium Small)
206
- prompt.input << " "
207
- prompt.input.rewind
208
- expect(prompt.select('What size?', choices, default: 2, enum: '.')).to eq('Medium')
209
- expect(prompt.output.string).to eq([
210
- "\e[?25lWhat size? \e[90m(Use arrow or number (1-3) keys, press Enter to select)\e[0m\n",
211
- " 1. Large\n",
212
- "\e[32m#{symbols[:pointer]} 2. Medium\e[0m\n",
213
- " 3. Small",
214
- "\e[2K\e[1G\e[1A" * 3,
215
- "\e[2K\e[1G",
216
- "What size? \e[32mMedium\e[0m\n\e[?25h"
217
- ].join)
218
- end
219
-
220
- it "changes selected item color & marker" do
221
- choices = %w(Large Medium Small)
222
- prompt.input << " "
223
- prompt.input.rewind
224
- options = {active_color: :blue, help_color: :red, marker: '>'}
225
- value = prompt.select('What size?', choices, options)
226
- expect(value).to eq('Large')
227
- expect(prompt.output.string).to eq([
228
- "\e[?25lWhat size? \e[31m(Use arrow keys, press Enter to select)\e[0m\n",
229
- "\e[34m> Large\e[0m\n",
230
- " Medium\n",
231
- " Small",
232
- "\e[2K\e[1G\e[1A" * 3,
233
- "\e[2K\e[1G",
234
- "What size? \e[34mLarge\e[0m\n\e[?25h"
235
- ].join)
236
- end
237
-
238
- it "changes help text" do
239
- choices = %w(Large Medium Small)
240
- prompt.input << " "
241
- prompt.input.rewind
242
- value = prompt.select('What size?', choices, help: "(Bash keyboard)")
243
- expect(value).to eq('Large')
244
- expect(prompt.output.string).to eq([
245
- "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
246
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
247
- " Medium\n",
248
- " Small",
249
- "\e[2K\e[1G\e[1A" * 3,
250
- "\e[2K\e[1G",
251
- "What size? \e[32mLarge\e[0m\n\e[?25h"
252
- ].join)
253
- end
254
-
255
- it "changes help text through DSL" do
256
- choices = %w(Large Medium Small)
257
- prompt.input << " "
258
- prompt.input.rewind
259
- value = prompt.select('What size?') do |menu|
260
- menu.help "(Bash keyboard)"
261
- menu.choices choices
262
- end
263
- expect(value).to eq('Large')
264
- expect(prompt.output.string).to eq([
265
- "\e[?25lWhat size? \e[90m(Bash keyboard)\e[0m\n",
266
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
267
- " Medium\n",
268
- " Small",
269
- "\e[2K\e[1G\e[1A" * 3,
270
- "\e[2K\e[1G",
271
- "What size? \e[32mLarge\e[0m\n\e[?25h"
272
- ].join)
273
- end
274
-
275
- it "sets prompt prefix" do
276
- prompt = TTY::TestPrompt.new(prefix: '[?] ')
277
- choices = %w(Large Medium Small)
278
- prompt.input << "\r"
279
- prompt.input.rewind
280
- expect(prompt.select('What size?', choices)).to eq('Large')
281
- expect(prompt.output.string).to eq([
282
- "\e[?25l[?] What size? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
283
- "\e[32m#{symbols[:pointer]} Large\e[0m\n",
284
- " Medium\n",
285
- " Small",
286
- "\e[2K\e[1G\e[1A" * 3,
287
- "\e[2K\e[1G",
288
- "[?] What size? \e[32mLarge\e[0m\n\e[?25h"
289
- ].join)
290
- end
291
-
292
- it "paginates long selections" do
293
- choices = %w(A B C D E F G H)
294
- prompt.input << "\r"
295
- prompt.input.rewind
296
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
297
- expect(value).to eq('D')
298
- expect(prompt.output.string).to eq([
299
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
300
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
301
- " E\n",
302
- " F\n",
303
- "\e[90m(Move up or down to reveal more choices)\e[0m",
304
- "\e[2K\e[1G\e[1A" * 4,
305
- "\e[2K\e[1G",
306
- "What letter? \e[32mD\e[0m\n\e[?25h",
307
- ].join)
308
- end
309
-
310
- it "paginates choices as hash object" do
311
- prompt = TTY::TestPrompt.new
312
- choices = {A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8}
313
- prompt.input << "\r"
314
- prompt.input.rewind
315
- value = prompt.select("What letter?", choices, per_page: 3, default: 4)
316
- expect(value).to eq(4)
317
- expect(prompt.output.string).to eq([
318
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
319
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
320
- " E\n",
321
- " F\n",
322
- "\e[90m(Move up or down to reveal more choices)\e[0m",
323
- "\e[2K\e[1G\e[1A" * 4,
324
- "\e[2K\e[1G",
325
- "What letter? \e[32mD\e[0m\n\e[?25h",
326
- ].join)
327
- end
328
-
329
- it "paginates long selections through DSL" do
330
- prompt = TTY::TestPrompt.new
331
- choices = %w(A B C D E F G H)
332
- prompt.input << "\r"
333
- prompt.input.rewind
334
- value = prompt.select("What letter?") do |menu|
335
- menu.per_page 3
336
- menu.page_help '(Wiggle thy finger up or down to see more)'
337
- menu.default 4
338
-
339
- menu.choices choices
340
- end
341
- expect(value).to eq('D')
342
- expect(prompt.output.string).to eq([
343
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
344
- "\e[32m#{symbols[:pointer]} D\e[0m\n",
345
- " E\n",
346
- " F\n",
347
- "\e[90m(Wiggle thy finger up or down to see more)\e[0m",
348
- "\e[2K\e[1G\e[1A" * 4,
349
- "\e[2K\e[1G",
350
- "What letter? \e[32mD\e[0m\n\e[?25h",
351
- ].join)
352
- end
353
-
354
- context 'with :cycle option' do
355
- it "doesn't cycle by default" do
356
- prompt = TTY::TestPrompt.new
357
- choices = %w(A B C)
358
- prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
359
- prompt.input << "j" << "j" << "j" << "\r"
360
- prompt.input.rewind
361
- value = prompt.select("What letter?", choices)
362
- expect(value).to eq("C")
363
- expect(prompt.output.string).to eq(
364
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
365
- output_helper("What letter?", choices, "B") +
366
- output_helper("What letter?", choices, "C") +
367
- output_helper("What letter?", choices, "C") +
368
- "What letter? \e[32mC\e[0m\n\e[?25h"
369
- )
370
- end
371
-
372
- it "cycles around when configured to do so" do
373
- prompt = TTY::TestPrompt.new
374
- choices = %w(A B C)
375
- prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
376
- prompt.input << "j" << "j" << "j" << "\r"
377
- prompt.input.rewind
378
- value = prompt.select("What letter?", choices, cycle: true)
379
- expect(value).to eq("A")
380
- expect(prompt.output.string).to eq(
381
- output_helper("What letter?", choices, "A", init: true, hint: "Use arrow keys, press Enter to select") +
382
- output_helper("What letter?", choices, "B") +
383
- output_helper("What letter?", choices, "C") +
384
- output_helper("What letter?", choices, "A") +
385
- "What letter? \e[32mA\e[0m\n\e[?25h"
386
- )
387
- end
388
-
389
- it "cycles around disabled items" do
390
- prompt = TTY::TestPrompt.new
391
- choices = [
392
- {name: 'A', disabled: '(out)'},
393
- {name: 'B'},
394
- {name: 'C', disabled: '(out)'},
395
- {name: 'D'},
396
- {name: 'E', disabled: '(out)'},
397
- ]
398
- prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
399
- prompt.input << "j" << "j" << "j" << "\r"
400
- prompt.input.rewind
401
- value = prompt.select("What letter?", choices, cycle: true, default: 2)
402
- expect(value).to eq("D")
403
-
404
- expected_output =
405
- output_helper("What letter?", choices, "B", init: true,
406
- hint: "Use arrow keys, press Enter to select") +
407
- output_helper("What letter?", choices, "D") +
408
- output_helper("What letter?", choices, "B") +
409
- output_helper("What letter?", choices, "D") +
410
- "What letter? \e[32mD\e[0m\n\e[?25h"
411
-
412
- expect(prompt.output.string).to eq(expected_output)
413
- end
414
- end
415
-
416
- it "verifies default index format" do
417
- prompt = TTY::TestPrompt.new
418
- choices = %w(Large Medium Small)
419
- prompt.input << "\r"
420
- prompt.input.rewind
421
-
422
- expect {
423
- prompt.select('What size?', choices, default: '')
424
- }.to raise_error(TTY::Prompt::ConfigurationError, /in range \(1 - 3\)/)
425
- end
426
-
427
- it "doesn't paginate short selections" do
428
- prompt = TTY::TestPrompt.new
429
- choices = %w(A B C D)
430
- prompt.input << "\r"
431
- prompt.input.rewind
432
- value = prompt.select("What letter?", choices, per_page: 4, default: 1)
433
- expect(value).to eq('A')
434
-
435
- expect(prompt.output.string).to eq([
436
- "\e[?25lWhat letter? \e[90m(Use arrow keys, press Enter to select)\e[0m\n",
437
- "\e[32m#{symbols[:pointer]} A\e[0m\n",
438
- " B\n",
439
- " C\n",
440
- " D",
441
- "\e[2K\e[1G\e[1A" * 4,
442
- "\e[2K\e[1G",
443
- "What letter? \e[32mA\e[0m\n\e[?25h",
444
- ].join)
445
- end
446
-
447
- it "verifies default index range" do
448
- prompt = TTY::TestPrompt.new
449
- choices = %w(Large Medium Small)
450
- prompt.input << "\r"
451
- prompt.input.rewind
452
-
453
- expect {
454
- prompt.select("What size?", choices, default: 10)
455
- }.to raise_error(TTY::Prompt::ConfigurationError, /`10` out of range \(1 - 3\)/)
456
- end
457
-
458
- context "with filter" do
459
- it "doesn't allow mixing enumeration and filter" do
460
- prompt = TTY::TestPrompt.new
461
-
462
- expect {
463
- prompt.select("What size?", [], enum: '.', filter: true)
464
- }.to raise_error(TTY::Prompt::ConfigurationError, "Enumeration can't be used with filter")
465
- end
466
-
467
- it "filters and chooses a uniquely matching entry, ignoring case" do
468
- prompt = TTY::TestPrompt.new
469
- prompt.input << "U" << "g" << "\r"
470
- prompt.input.rewind
471
-
472
- answer = prompt.select("What size?", %w(Small Medium Large Huge), filter: true)
473
- expect(answer).to eql("Huge")
474
-
475
- actual_prompt_output = prompt.output.string
476
-
477
- expected_prompt_output =
478
- 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") +
479
- output_helper("What size?", %w(Medium Huge), "Medium", hint: 'Filter: "U"') +
480
- output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Ug"') +
481
- exit_message("What size?", "Huge")
482
-
483
- expect(actual_prompt_output).to eql(expected_prompt_output)
484
- end
485
-
486
- it "filters and chooses the first of multiple matching entries" do
487
- prompt = TTY::TestPrompt.new
488
- prompt.input << "g" << "\r"
489
- prompt.input.rewind
490
-
491
- answer = prompt.select("What size?", %w(Small Medium Large Huge), filter: true)
492
- expect(answer).to eql("Large")
493
-
494
- actual_prompt_output = prompt.output.string
495
- expected_prompt_output =
496
- 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") +
497
- output_helper("What size?", %w(Large Huge), "Large", hint: 'Filter: "g"') +
498
- exit_message("What size?", "Large")
499
-
500
- expect(actual_prompt_output).to eql(expected_prompt_output)
501
- end
502
-
503
- it "filters based on alphanumeric and punctuation characters" do
504
- prompt = TTY::TestPrompt.new
505
- prompt.input << "p" << "*" << "2" << "\r"
506
- prompt.input.rewind
507
-
508
- answer = prompt.select("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), filter: true)
509
- expect(answer).to eql("p*2@mail.com")
510
-
511
- actual_prompt_output = prompt.output.string
512
- expected_prompt_output =
513
- 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") +
514
- output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p"') +
515
- output_helper("What email?", %w(p*1@mail.com p*2@mail.com p*3@mail.com), "p*1@mail.com", hint: 'Filter: "p*"') +
516
- output_helper("What email?", %w(p*2@mail.com), "p*2@mail.com", hint: 'Filter: "p*2"') +
517
- exit_message("What email?", "p*2@mail.com")
518
-
519
- expect(actual_prompt_output).to eql(expected_prompt_output)
520
- end
521
-
522
- # This test can't be done in an exact way, at least, with the current framework
523
- it "doesn't exit when there are no matching entries" do
524
- prompt = TTY::TestPrompt.new
525
- prompt.on(:keypress) { |e| prompt.trigger(:keybackspace) if e.value == "a" }
526
- prompt.input << "z" << "\r" # shows no entry, blocking exit
527
- prompt.input << "a" << "\r" # triggers Backspace before `a` (see above)
528
- prompt.input.rewind
529
-
530
- answer = prompt.select("What size?", %w(Tiny Medium Large Huge), filter: true)
531
- expect(answer).to eql("Large")
532
-
533
- actual_prompt_output = prompt.output.string
534
- expected_prompt_output =
535
- 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") +
536
- output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
537
- output_helper("What size?", %w(), "", hint: 'Filter: "z"') +
538
- output_helper("What size?", %w(Large), "Large", hint: 'Filter: "a"') +
539
- exit_message("What size?", "Large")
540
-
541
- expect(actual_prompt_output).to eql(expected_prompt_output)
542
- end
543
-
544
- it "cancels a selection" do
545
- prompt = TTY::TestPrompt.new
546
- prompt.on(:keypress) { |e| prompt.trigger(:keydelete) if e.value == "S" }
547
- prompt.input << "Hu"
548
- prompt.input << "S" # triggers Canc before `S` (see above)
549
- prompt.input << "\r"
550
- prompt.input.rewind
551
-
552
- answer = prompt.select("What size?", %w(Small Medium Large Huge), filter: true)
553
- expect(answer).to eql("Small")
554
-
555
- expected_prompt_output =
556
- 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") +
557
- output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "H"') +
558
- output_helper("What size?", %w(Huge), "Huge", hint: 'Filter: "Hu"') +
559
- output_helper("What size?", %w(Small), "Small", hint: 'Filter: "S"') +
560
- exit_message("What size?", "Small")
561
-
562
- expect(prompt.output.string).to eql(expected_prompt_output)
563
- end
564
- end
565
-
566
- context 'with :disabled choice' do
567
- it "omits disabled choice when navigating menu" do
568
- choices = [ 'Small', 'Medium', {name: 'Large', disabled: '(out of stock)'}, 'Huge' ]
569
- prompt = TTY::TestPrompt.new
570
- prompt.input << "j" << "j" << "\r"
571
- prompt.input.rewind
572
- prompt.on(:keypress) { |e| prompt.trigger(:keydown) if e.value == "j" }
573
-
574
- answer = prompt.select("What size?", choices)
575
- expect(answer).to eq('Huge')
576
-
577
- expected_output =
578
- output_helper("What size?", choices, "Small", init: true,
579
- hint: "Use arrow keys, press Enter to select") +
580
- output_helper("What size?", choices, "Medium") +
581
- output_helper("What size?", choices, "Huge") +
582
- "What size? \e[32mHuge\e[0m\n\e[?25h"
583
-
584
- expect(prompt.output.string).to eq(expected_output)
585
- end
586
-
587
- it "doesn't show disabled choice when filtering choices" do
588
- choices = [ 'A', 'B', {name: 'C', disabled: '(unavailable)'}, 'D' ]
589
- prompt = TTY::TestPrompt.new
590
- prompt.on(:keypress) { |e| prompt.trigger(:keybackspace) if e.value == "a" }
591
- prompt.input << "c" << "\r" # nothing matches
592
- prompt.input << "a" << "\r" # backtracks & chooses default option
593
- prompt.input.rewind
594
-
595
- answer = prompt.select("What letter?", choices, filter: true)
596
- expect(answer).to eq('A')
597
-
598
- expected_output =
599
- output_helper("What letter?", choices, "A", init: true,
600
- hint: "Use arrow keys, press Enter to select, and letter keys to filter") +
601
- output_helper("What letter?", [], "", hint: 'Filter: "c"') +
602
- output_helper("What letter?", [], "", hint: 'Filter: "c"') +
603
- output_helper("What letter?", ['A'], "A", hint: 'Filter: "a"') +
604
- exit_message("What letter?", "A")
605
-
606
- expect(prompt.output.string).to eq(expected_output)
607
- end
608
-
609
- it "omits disabled choice when number key is pressed" do
610
- choices = [ 'Small', {name: 'Medium', disabled: '(out of stock)'}, 'Large' ]
611
- prompt = TTY::TestPrompt.new
612
- prompt.input << "2" << "\r" << "\r"
613
- prompt.input.rewind
614
- answer = prompt.select('What size?') do |menu|
615
- menu.enum ')'
616
-
617
- menu.choice 'Small', 1
618
- menu.choice 'Medium', 2, disabled: '(out of stock)'
619
- menu.choice 'Large', 3
620
- end
621
- expect(answer).to eq(1)
622
-
623
- expected_output =
624
- output_helper("What size?", choices, "Small", init: true, enum: ') ',
625
- hint: "Use arrow or number (1-3) keys, press Enter to select") +
626
- output_helper("What size?", choices, "Small", enum: ') ') +
627
- "What size? \e[32mSmall\e[0m\n\e[?25h"
628
-
629
- expect(prompt.output.string).to eq(expected_output)
630
- end
631
-
632
- it "sets default correct when disabled choice" do
633
- choices = [ {name: 'Small', disabled: '(out of stock)'}, 'Medium', 'Large', 'Huge' ]
634
- prompt = TTY::TestPrompt.new
635
- prompt.input << "\r"
636
- prompt.input.rewind
637
-
638
- expect {
639
- prompt.select("What size?", choices)
640
- }.to raise_error(TTY::Prompt::ConfigurationError, /default index `1` matches disabled choice item/)
641
- end
642
- end
643
- end