tty-prompt 0.12.0 → 0.13.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/CHANGELOG.md +12 -0
  4. data/README.md +3 -3
  5. data/appveyor.yml +2 -0
  6. data/lib/tty/prompt/keypress.rb +7 -4
  7. data/lib/tty/prompt/reader.rb +1 -1
  8. data/lib/tty/prompt/reader/win_api.rb +14 -0
  9. data/lib/tty/prompt/reader/win_console.rb +9 -2
  10. data/lib/tty/prompt/timeout.rb +44 -23
  11. data/lib/tty/prompt/version.rb +1 -1
  12. data/tty-prompt.gemspec +6 -3
  13. metadata +21 -131
  14. data/spec/spec_helper.rb +0 -45
  15. data/spec/unit/ask_spec.rb +0 -132
  16. data/spec/unit/choice/eql_spec.rb +0 -22
  17. data/spec/unit/choice/from_spec.rb +0 -29
  18. data/spec/unit/choices/add_spec.rb +0 -12
  19. data/spec/unit/choices/each_spec.rb +0 -13
  20. data/spec/unit/choices/find_by_spec.rb +0 -10
  21. data/spec/unit/choices/new_spec.rb +0 -10
  22. data/spec/unit/choices/pluck_spec.rb +0 -9
  23. data/spec/unit/collect_spec.rb +0 -33
  24. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  25. data/spec/unit/converters/convert_char_spec.rb +0 -11
  26. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  27. data/spec/unit/converters/convert_date_spec.rb +0 -34
  28. data/spec/unit/converters/convert_file_spec.rb +0 -17
  29. data/spec/unit/converters/convert_number_spec.rb +0 -39
  30. data/spec/unit/converters/convert_path_spec.rb +0 -18
  31. data/spec/unit/converters/convert_range_spec.rb +0 -22
  32. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  33. data/spec/unit/converters/convert_string_spec.rb +0 -21
  34. data/spec/unit/converters/on_error_spec.rb +0 -9
  35. data/spec/unit/distance/distance_spec.rb +0 -73
  36. data/spec/unit/enum_paginator_spec.rb +0 -65
  37. data/spec/unit/enum_select_spec.rb +0 -310
  38. data/spec/unit/error_spec.rb +0 -20
  39. data/spec/unit/evaluator_spec.rb +0 -67
  40. data/spec/unit/expand_spec.rb +0 -198
  41. data/spec/unit/keypress_spec.rb +0 -58
  42. data/spec/unit/mask_spec.rb +0 -132
  43. data/spec/unit/multi_select_spec.rb +0 -319
  44. data/spec/unit/multiline_spec.rb +0 -77
  45. data/spec/unit/new_spec.rb +0 -20
  46. data/spec/unit/ok_spec.rb +0 -10
  47. data/spec/unit/paginator_spec.rb +0 -63
  48. data/spec/unit/question/checks_spec.rb +0 -97
  49. data/spec/unit/question/default_spec.rb +0 -31
  50. data/spec/unit/question/echo_spec.rb +0 -38
  51. data/spec/unit/question/in_spec.rb +0 -115
  52. data/spec/unit/question/initialize_spec.rb +0 -12
  53. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  54. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  55. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  56. data/spec/unit/question/modify_spec.rb +0 -41
  57. data/spec/unit/question/required_spec.rb +0 -92
  58. data/spec/unit/question/validate_spec.rb +0 -115
  59. data/spec/unit/question/validation/call_spec.rb +0 -31
  60. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  61. data/spec/unit/reader/history_spec.rb +0 -172
  62. data/spec/unit/reader/key_event_spec.rb +0 -86
  63. data/spec/unit/reader/line_spec.rb +0 -110
  64. data/spec/unit/reader/publish_keypress_event_spec.rb +0 -94
  65. data/spec/unit/reader/read_keypress_spec.rb +0 -80
  66. data/spec/unit/reader/read_line_spec.rb +0 -68
  67. data/spec/unit/reader/read_multiline_spec.rb +0 -60
  68. data/spec/unit/result_spec.rb +0 -40
  69. data/spec/unit/say_spec.rb +0 -67
  70. data/spec/unit/select_spec.rb +0 -359
  71. data/spec/unit/slider_spec.rb +0 -96
  72. data/spec/unit/statement/initialize_spec.rb +0 -15
  73. data/spec/unit/suggest_spec.rb +0 -28
  74. data/spec/unit/warn_spec.rb +0 -21
  75. data/spec/unit/yes_no_spec.rb +0 -235
@@ -1,92 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe TTY::Prompt::Question, '#required' do
4
-
5
- subject(:prompt) { TTY::TestPrompt.new }
6
-
7
- it 'requires value to be present' do
8
- prompt.input << "Piotr"
9
- prompt.input.rewind
10
- prompt.ask('What is your name?') { |q| q.required(true) }
11
- expect(prompt.output.string).to eq([
12
- "What is your name? ",
13
- "\e[2K\e[1GWhat is your name? P",
14
- "\e[2K\e[1GWhat is your name? Pi",
15
- "\e[2K\e[1GWhat is your name? Pio",
16
- "\e[2K\e[1GWhat is your name? Piot",
17
- "\e[2K\e[1GWhat is your name? Piotr",
18
- "\e[1A\e[2K\e[1G",
19
- "What is your name? \e[32mPiotr\e[0m\n"
20
- ].join)
21
- end
22
-
23
- it 'requires value to be present with option' do
24
- prompt.input << " \nPiotr"
25
- prompt.input.rewind
26
- prompt.ask('What is your name?', required: true)
27
- expect(prompt.output.string).to eq([
28
- "What is your name? ",
29
- "\e[2K\e[1GWhat is your name? ",
30
- "\e[2K\e[1GWhat is your name? ",
31
- "\e[2K\e[1GWhat is your name? \n",
32
- "\e[31m>>\e[0m Value must be provided\e[1A",
33
- "\e[2K\e[1G",
34
- "What is your name? ",
35
- "\e[2K\e[1GWhat is your name? P",
36
- "\e[2K\e[1GWhat is your name? Pi",
37
- "\e[2K\e[1GWhat is your name? Pio",
38
- "\e[2K\e[1GWhat is your name? Piot",
39
- "\e[2K\e[1GWhat is your name? Piotr",
40
- "\e[2K\e[1G",
41
- "\e[1A\e[2K\e[1G",
42
- "What is your name? \e[32mPiotr\e[0m\n"
43
- ].join)
44
- end
45
-
46
- it "doesn't require value to be present" do
47
- prompt.input << ''
48
- prompt.input.rewind
49
- answer = prompt.ask('What is your name?') { |q| q.required(false) }
50
- expect(answer).to be_nil
51
- end
52
-
53
- it "uses required in validation check" do
54
- prompt.input << " \nexists\ntest\n"
55
- prompt.input.rewind
56
- answer = prompt.ask('File name?') do |q|
57
- q.required(true)
58
- q.validate { |v| !(v =~ /exists/) }
59
- q.messages[:required?] = 'File name must not be empty!'
60
- q.messages[:valid?] = 'File already exists!'
61
- end
62
- expect(answer).to eq('test')
63
- expect(prompt.output.string).to eq([
64
- "File name? ",
65
- "\e[2K\e[1GFile name? ",
66
- "\e[2K\e[1GFile name? ",
67
- "\e[2K\e[1GFile name? \n",
68
- "\e[31m>>\e[0m File name must not be empty!",
69
- "\e[1A\e[2K\e[1G",
70
- "File name? ",
71
- "\e[2K\e[1GFile name? e",
72
- "\e[2K\e[1GFile name? ex",
73
- "\e[2K\e[1GFile name? exi",
74
- "\e[2K\e[1GFile name? exis",
75
- "\e[2K\e[1GFile name? exist",
76
- "\e[2K\e[1GFile name? exists",
77
- "\e[2K\e[1GFile name? exists\n",
78
- "\e[31m>>\e[0m File already exists!",
79
- "\e[1A\e[2K\e[1G",
80
- "File name? ",
81
- "\e[2K\e[1GFile name? t",
82
- "\e[2K\e[1GFile name? te",
83
- "\e[2K\e[1GFile name? tes",
84
- "\e[2K\e[1GFile name? test",
85
- "\e[2K\e[1GFile name? test\n",
86
- "\e[2K\e[1G",
87
- "\e[1A\e[2K\e[1G",
88
- "File name? \e[32mtest\e[0m\n",
89
- ].join)
90
- expect(answer).to eq('test')
91
- end
92
- end
@@ -1,115 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe TTY::Prompt::Question, '#validate' do
4
-
5
- subject(:prompt) { TTY::TestPrompt.new }
6
-
7
- it 'validates input with regex' do
8
- prompt.input << 'p.m'
9
- prompt.input.rewind
10
-
11
- answer = prompt.ask('What is your username?') do |q|
12
- q.validate(/^[^\.]+\.[^\.]+/)
13
- end
14
-
15
- expect(answer).to eq('p.m')
16
- expect(prompt.output.string).to eq([
17
- "What is your username? ",
18
- "\e[2K\e[1GWhat is your username? p",
19
- "\e[2K\e[1GWhat is your username? p.",
20
- "\e[2K\e[1GWhat is your username? p.m",
21
- "\e[1A\e[2K\e[1G",
22
- "What is your username? \e[32mp.m\e[0m\n"
23
- ].join)
24
- end
25
-
26
- it 'validates input with proc' do
27
- prompt.input << 'piotr.murach'
28
- prompt.input.rewind
29
-
30
- answer = prompt.ask('What is your username?') do |q|
31
- q.validate { |input| input =~ /^[^\.]+\.[^\.]+/ }
32
- end
33
-
34
- expect(answer).to eq('piotr.murach')
35
- end
36
-
37
- it 'understands custom validation like :email' do
38
- prompt.input << 'piotr@example.com'
39
- prompt.input.rewind
40
-
41
- answer = prompt.ask('What is your email?') do |q|
42
- q.validate :email
43
- end
44
-
45
- expect(answer).to eq('piotr@example.com')
46
- end
47
-
48
- it "provides default error message for wrong input" do
49
- prompt.input << "wrong\np@m.com\n"
50
- prompt.input.rewind
51
-
52
- answer = prompt.ask('What is your email?') do |q|
53
- q.validate :email
54
- end
55
-
56
- expect(answer).to eq('p@m.com')
57
- expect(prompt.output.string).to eq([
58
- "What is your email? ",
59
- "\e[2K\e[1GWhat is your email? w",
60
- "\e[2K\e[1GWhat is your email? wr",
61
- "\e[2K\e[1GWhat is your email? wro",
62
- "\e[2K\e[1GWhat is your email? wron",
63
- "\e[2K\e[1GWhat is your email? wrong",
64
- "\e[2K\e[1GWhat is your email? wrong\n",
65
- "\e[31m>>\e[0m Your answer is invalid (must match :email)\e[1A",
66
- "\e[2K\e[1G",
67
- "What is your email? ",
68
- "\e[2K\e[1GWhat is your email? p",
69
- "\e[2K\e[1GWhat is your email? p@",
70
- "\e[2K\e[1GWhat is your email? p@m",
71
- "\e[2K\e[1GWhat is your email? p@m.",
72
- "\e[2K\e[1GWhat is your email? p@m.c",
73
- "\e[2K\e[1GWhat is your email? p@m.co",
74
- "\e[2K\e[1GWhat is your email? p@m.com",
75
- "\e[2K\e[1GWhat is your email? p@m.com\n",
76
- "\e[2K\e[1G",
77
- "\e[1A\e[2K\e[1G",
78
- "What is your email? \e[32mp@m.com\e[0m\n"
79
- ].join)
80
- end
81
-
82
- it "provides custom error message for wrong input" do
83
- prompt.input << "wrong\np@m.com"
84
- prompt.input.rewind
85
-
86
- answer = prompt.ask('What is your email?') do |q|
87
- q.validate :email
88
- q.messages[:valid?] = 'Not an email!'
89
- end
90
-
91
- expect(answer).to eq('p@m.com')
92
- expect(prompt.output.string).to eq([
93
- "What is your email? ",
94
- "\e[2K\e[1GWhat is your email? w",
95
- "\e[2K\e[1GWhat is your email? wr",
96
- "\e[2K\e[1GWhat is your email? wro",
97
- "\e[2K\e[1GWhat is your email? wron",
98
- "\e[2K\e[1GWhat is your email? wrong",
99
- "\e[2K\e[1GWhat is your email? wrong\n",
100
- "\e[31m>>\e[0m Not an email!\e[1A",
101
- "\e[2K\e[1G",
102
- "What is your email? ",
103
- "\e[2K\e[1GWhat is your email? p",
104
- "\e[2K\e[1GWhat is your email? p@",
105
- "\e[2K\e[1GWhat is your email? p@m",
106
- "\e[2K\e[1GWhat is your email? p@m.",
107
- "\e[2K\e[1GWhat is your email? p@m.c",
108
- "\e[2K\e[1GWhat is your email? p@m.co",
109
- "\e[2K\e[1GWhat is your email? p@m.com",
110
- "\e[2K\e[1G",
111
- "\e[1A\e[2K\e[1G",
112
- "What is your email? \e[32mp@m.com\e[0m\n"
113
- ].join)
114
- end
115
- end
@@ -1,31 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe TTY::Prompt::Question::Validation, '#call' do
4
- let(:pattern) { /^[^\.]+\.[^\.]+/ }
5
-
6
- it "validates nil input" do
7
- validation = described_class.new(pattern)
8
- expect(validation.(nil)).to eq(false)
9
- end
10
-
11
- it "validates successfully when the value matches pattern" do
12
- validation = described_class.new(pattern)
13
- expect(validation.('piotr.murach')).to eq(true)
14
- end
15
-
16
- it "validates with a proc" do
17
- pat = proc { |input| !pattern.match(input).nil? }
18
- validation = described_class.new(pat)
19
- expect(validation.call('piotr.murach')).to eq(true)
20
- end
21
-
22
- it "validates with custom name" do
23
- validation = described_class.new(:email)
24
- expect(validation.call('piotr@example.com')).to eq(true)
25
- end
26
-
27
- it "fails validation when not maching pattern" do
28
- validation = described_class.new(pattern)
29
- expect(validation.('piotrmurach')).to eq(false)
30
- end
31
- end
@@ -1,30 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe TTY::Prompt::Question::Validation, '#coerce' do
4
- let(:instance) { described_class.new }
5
-
6
- it "coerces lambda into proc" do
7
- pattern = lambda { "^[^\.]+\.[^\.]+" }
8
- validation = described_class.new(pattern)
9
- expect(validation.pattern).to be_a(Proc)
10
- end
11
-
12
- it "doesn't coerce symbols" do
13
- pattern = :email
14
- validation =described_class.new(pattern)
15
- expect(validation.pattern).to eq(:email)
16
- end
17
-
18
- it "coerces into regex" do
19
- pattern = /^[^\.]+\.[^\.]+/
20
- validation = described_class.new(pattern)
21
- expect(validation.pattern).to be_a(Regexp)
22
- end
23
-
24
- it "fails to coerce pattern into validation" do
25
- pattern = Object.new
26
- expect {
27
- described_class.new(pattern)
28
- }.to raise_error(TTY::Prompt::ValidationCoercion)
29
- end
30
- end
@@ -1,172 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe TTY::Prompt::Reader::History do
4
- it "has no lines" do
5
- history = described_class.new
6
- expect(history.size).to eq(0)
7
- end
8
-
9
- it "doesn't navigate through empty buffer" do
10
- history = described_class.new
11
- expect(history.next?).to eq(false)
12
- expect(history.previous?).to eq(false)
13
- end
14
-
15
- it "allows to cycle through non-empty buffer" do
16
- history = described_class.new(3, {cycle: true})
17
- history << "line"
18
- expect(history.next?).to eq(true)
19
- expect(history.previous?).to eq(true)
20
- end
21
-
22
- it "defaults maximum size" do
23
- history = described_class.new
24
- expect(history.max_size).to eq(512)
25
- end
26
-
27
- it "presents string representation" do
28
- history = described_class.new
29
- expect(history.to_s).to eq("[]")
30
- end
31
-
32
- it "adds items to history without overflowing" do
33
- history = described_class.new(3)
34
- history << "line #1"
35
- history << "line #2"
36
- history << "line #3"
37
- history << "line #4"
38
-
39
- expect(history.to_a).to eq(["line #2", "line #3", "line #4"])
40
- expect(history.index).to eq(2)
41
- end
42
-
43
- it "excludes items" do
44
- exclude = proc { |line| /line #[23]/.match(line) }
45
- history = described_class.new(3, exclude: exclude)
46
- history << "line #1"
47
- history << "line #2"
48
- history << "line #3"
49
-
50
- expect(history.to_a).to eq(["line #1"])
51
- expect(history.index).to eq(0)
52
- end
53
-
54
- it "allows duplicates" do
55
- history = described_class.new(3)
56
- history << "line #1"
57
- history << "line #1"
58
- history << "line #1"
59
-
60
- expect(history.to_a).to eq(["line #1", "line #1", "line #1"])
61
- end
62
-
63
- it "prevents duplicates" do
64
- history = described_class.new(3, duplicates: false)
65
- history << "line #1"
66
- history << "line #1"
67
- history << "line #1"
68
-
69
- expect(history.to_a).to eq(["line #1"])
70
- end
71
-
72
- it "navigates through history buffer without cycling" do
73
- history = described_class.new(3)
74
- history << "line #1"
75
- history << "line #2"
76
- history << "line #3"
77
-
78
- expect(history.index).to eq(2)
79
- history.previous
80
- history.previous
81
- expect(history.index).to eq(0)
82
- history.previous
83
- expect(history.index).to eq(0)
84
- history.next
85
- history.next
86
- expect(history.index).to eq(2)
87
- history.next
88
- expect(history.next?).to eq(false)
89
- expect(history.index).to eq(2)
90
- end
91
-
92
- it "navigates through history buffer with cycling" do
93
- history = described_class.new(3, cycle: true)
94
- history << "line #1"
95
- history << "line #2"
96
- history << "line #3"
97
-
98
- expect(history.index).to eq(2)
99
- history.previous
100
- history.previous
101
- expect(history.index).to eq(0)
102
- history.previous
103
- expect(history.index).to eq(2)
104
- expect(history.next?).to eq(true)
105
- history.next
106
- history.next
107
- expect(history.index).to eq(1)
108
- history.next
109
- expect(history.index).to eq(2)
110
- end
111
-
112
- it "checks if navigation is possible" do
113
- history = described_class.new(3)
114
- expect(history.previous?).to eq(false)
115
- expect(history.next?).to eq(false)
116
-
117
- history << "line #1"
118
- history << "line #2"
119
- expect(history.previous?).to eq(true)
120
- expect(history.next?).to eq(false)
121
-
122
- history.previous
123
- expect(history.previous?).to eq(true)
124
- expect(history.next?).to eq(true)
125
-
126
- history.previous
127
- expect(history.previous?).to eq(true)
128
- expect(history.next?).to eq(true)
129
- end
130
-
131
- it "gets line based on index" do
132
- history = described_class.new(3, cycle: true)
133
- history << "line #1"
134
- history << "line #2"
135
- history << "line #3"
136
-
137
- expect(history[-1]).to eq('line #3')
138
- expect(history[1]).to eq('line #2')
139
- expect {
140
- history[11]
141
- }.to raise_error(IndexError, 'invalid index')
142
- end
143
-
144
- it "retrieves current line" do
145
- history = described_class.new(3, cycle: true)
146
- expect(history.get).to eq(nil)
147
-
148
- history << "line #1"
149
- history << "line #2"
150
- history << "line #3"
151
-
152
- expect(history.get).to eq("line #3")
153
- history.previous
154
- history.previous
155
- expect(history.get).to eq("line #1")
156
- history.next
157
- expect(history.get).to eq("line #2")
158
- end
159
-
160
- it "clears all lines" do
161
- history = described_class.new(3)
162
-
163
- history << "line #1"
164
- history << "line #2"
165
- history << "line #3"
166
-
167
- expect(history.size).to eq(3)
168
- history.clear
169
- expect(history.size).to eq(0)
170
- expect(history.index).to eq(0)
171
- end
172
- end
@@ -1,86 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'shellwords'
4
- require 'tty/prompt/reader/codes'
5
-
6
- RSpec.describe TTY::Prompt::Reader::KeyEvent, '#from' do
7
- let(:keys) { TTY::Prompt::Reader::Codes.keys }
8
-
9
- it "parses backspace" do
10
- event = described_class.from(keys, "\x7f")
11
- expect(event.key.name).to eq(:backspace)
12
- expect(event.value).to eq("\x7f")
13
- end
14
-
15
- it "parses lowercase char" do
16
- event = described_class.from(keys, 'a')
17
- expect(event.key.name).to eq(:alpha)
18
- expect(event.value).to eq('a')
19
- end
20
-
21
- it "parses uppercase char" do
22
- event = described_class.from(keys, 'A')
23
- expect(event.key.name).to eq(:alpha)
24
- expect(event.value).to eq('A')
25
- end
26
-
27
- it "parses ctrl-a to ctrl-z inputs" do
28
- (1..26).zip('a'..'z').each do |code, char|
29
- next if ['i', 'j', 'm'].include?(char)
30
- event = described_class.from(keys, code.chr)
31
- expect(event.key.name).to eq(:"ctrl_#{char}")
32
- expect(event.value).to eq(code.chr)
33
- end
34
- end
35
-
36
- # F1-F12 keys
37
- {
38
- f1: ["\eOP", "\e[11~"],
39
- f2: ["\eOQ", "\e[12~"],
40
- f3: ["\eOR", "\e[13~"],
41
- f4: ["\eOS", "\e[14~"],
42
- f5: [ "\e[15~"],
43
- f6: [ "\e[17~"],
44
- f7: [ "\e[18~"],
45
- f8: [ "\e[19~"],
46
- f9: [ "\e[20~"],
47
- f10: [ "\e[21~"],
48
- f11: [ "\e[23~"],
49
- f12: [ "\e[24~"]
50
- }.each do |name, codes|
51
- codes.each do |code|
52
- it "parses #{Shellwords.escape(code)} as #{name} key" do
53
- event = described_class.from(keys, code)
54
- expect(event.key.name).to eq(name)
55
- expect(event.key.meta).to eq(false)
56
- expect(event.key.ctrl).to eq(false)
57
- expect(event.key.shift).to eq(false)
58
- end
59
- end
60
- end
61
-
62
- # arrow keys & text editing
63
- {
64
- up: ["\e[A"],
65
- down: ["\e[B"],
66
- right: ["\e[C"],
67
- left: ["\e[D"],
68
- clear: ["\e[E"],
69
- home: ["\e[1~"],
70
- insert: ["\e[2~"],
71
- delete: ["\e[3~"],
72
- end: ["\e[4~"],
73
- page_up: ["\e[5~"],
74
- page_down: ["\e[6~"]
75
- }.each do |name, codes|
76
- codes.each do |code|
77
- it "parses #{Shellwords.escape(code)} as #{name} key" do
78
- event = described_class.from(keys, code)
79
- expect(event.key.name).to eq(name)
80
- expect(event.key.meta).to eq(false)
81
- expect(event.key.ctrl).to eq(false)
82
- expect(event.key.shift).to eq(false)
83
- end
84
- end
85
- end
86
- end