tty-prompt 0.18.1 → 0.23.0

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