tty-prompt 0.18.0 → 0.22.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 (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