tty-prompt 0.18.1 → 0.23.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 (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