clin 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/.lint-ci.yml +2 -0
- data/.simplecov +5 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +11 -0
- data/README.md +5 -4
- data/benchmarks/bench.rb +21 -0
- data/benchmarks/text_bench.rb +78 -0
- data/clin.gemspec +2 -1
- data/examples/reusable_options.rb +19 -0
- data/examples/simple.rb +8 -3
- data/examples/test.rb +5 -5
- data/examples/text_builder.rb +40 -0
- data/lib/clin/argument.rb +19 -2
- data/lib/clin/command_mixin/core.rb +13 -18
- data/lib/clin/command_mixin/options.rb +37 -26
- data/lib/clin/command_parser.rb +46 -57
- data/lib/clin/common/help_options.rb +1 -0
- data/lib/clin/errors.rb +50 -4
- data/lib/clin/line_reader/basic.rb +38 -0
- data/lib/clin/line_reader/readline.rb +53 -0
- data/lib/clin/line_reader.rb +16 -0
- data/lib/clin/option.rb +24 -11
- data/lib/clin/option_parser.rb +159 -0
- data/lib/clin/shell.rb +36 -15
- data/lib/clin/shell_interaction/choose.rb +19 -11
- data/lib/clin/shell_interaction/file_conflict.rb +4 -1
- data/lib/clin/shell_interaction/select.rb +44 -0
- data/lib/clin/shell_interaction.rb +1 -0
- data/lib/clin/text/table.rb +270 -0
- data/lib/clin/text.rb +152 -0
- data/lib/clin/version.rb +1 -1
- data/lib/clin.rb +10 -1
- data/spec/clin/command_dispacher_spec.rb +1 -1
- data/spec/clin/command_mixin/options_spec.rb +38 -15
- data/spec/clin/command_parser_spec.rb +27 -51
- data/spec/clin/line_reader/basic_spec.rb +54 -0
- data/spec/clin/line_reader/readline_spec.rb +64 -0
- data/spec/clin/line_reader_spec.rb +17 -0
- data/spec/clin/option_parser_spec.rb +217 -0
- data/spec/clin/option_spec.rb +5 -7
- data/spec/clin/shell_interaction/choose_spec.rb +30 -0
- data/spec/clin/shell_interaction/file_interaction_spec.rb +18 -0
- data/spec/clin/shell_interaction/select_spec.rb +96 -0
- data/spec/clin/shell_spec.rb +42 -0
- data/spec/clin/text/table_cell_spec.rb +72 -0
- data/spec/clin/text/table_row_spec.rb +74 -0
- data/spec/clin/text/table_separator_row_spec.rb +82 -0
- data/spec/clin/text/table_spec.rb +259 -0
- data/spec/clin/text_spec.rb +158 -0
- data/spec/examples/list_option_spec.rb +6 -2
- data/spec/examples/reusable_options_spec.rb +21 -0
- data/spec/examples/simple_spec.rb +9 -9
- data/spec/spec_helper.rb +3 -2
- metadata +54 -3
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clin::LineReader::Basic do
|
4
|
+
let(:shell) { Clin::Shell.new }
|
5
|
+
describe '#readline' do
|
6
|
+
it 'uses $stdin and $stdout to get input from the user' do
|
7
|
+
expect(shell.out).to receive(:print).with('Where are you from? ')
|
8
|
+
expect(shell.in).to receive(:gets).and_return('France')
|
9
|
+
expect(shell.in).not_to receive(:noecho)
|
10
|
+
reader = Clin::LineReader::Basic.new(shell, 'Where are you from? ', {})
|
11
|
+
expect(reader.readline).to eq('France')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'disables echo when asked to' do
|
15
|
+
expect(shell.out).to receive(:print).with('Where are you from? ')
|
16
|
+
noecho_stdin = double('noecho_stdin')
|
17
|
+
expect(noecho_stdin).to receive(:gets).and_return('Asgard')
|
18
|
+
expect(shell.in).to receive(:noecho).and_yield(noecho_stdin)
|
19
|
+
reader = Clin::LineReader::Basic.new(shell, 'Where are you from? ', echo: false)
|
20
|
+
expect(reader.readline).to eq('Asgard')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.available?' do
|
25
|
+
it 'returns is always available' do
|
26
|
+
expect(Clin::LineReader::Basic).to be_available
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#scan' do
|
31
|
+
it 'call in#gets when echo is true' do
|
32
|
+
reader = Clin::LineReader::Basic.new(shell, 'Where are you from? ', {echo: true})
|
33
|
+
expect(shell.in).to receive(:gets)
|
34
|
+
expect(shell.in).not_to receive(:noecho)
|
35
|
+
reader.send(:scan)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'call in#noecho(&:gets) when echo is false' do
|
39
|
+
reader = Clin::LineReader::Basic.new(shell, 'Where are you from? ', {echo: false})
|
40
|
+
expect(shell.in).not_to receive(:gets)
|
41
|
+
expect(shell.in).to receive(:noecho)
|
42
|
+
reader.send(:scan)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'call in#gets if the console does not support noecho' do
|
46
|
+
reader = Clin::LineReader::Basic.new(shell, 'Where are you from? ', {echo: false})
|
47
|
+
expect(shell.in).to receive(:noecho) do
|
48
|
+
fail Errno::EBADF
|
49
|
+
end
|
50
|
+
expect(shell.in).to receive(:gets)
|
51
|
+
reader.send(:scan)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clin::LineReader::Readline do
|
4
|
+
let(:shell) { Clin::Shell.new }
|
5
|
+
describe '#readline' do
|
6
|
+
it 'call Readline.readline' do
|
7
|
+
expect(Readline).to receive(:readline).with('> ', true).and_return('val')
|
8
|
+
expect(Readline).not_to receive(:completion_proc=)
|
9
|
+
subject = Clin::LineReader::Readline.new(shell, '> ', {})
|
10
|
+
expect(subject.readline).to eq('val')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'provides tab completion when given a limited_to option' do
|
14
|
+
countries = ['United States', 'France', 'United Kingdom']
|
15
|
+
expect(Readline).to receive(:readline)
|
16
|
+
expect(Readline).to receive(:completion_proc=) do |proc|
|
17
|
+
expect(proc.call('')).to eq countries
|
18
|
+
expect(proc.call('U')).to eq ['United States', 'United Kingdom']
|
19
|
+
expect(proc.call('United S')).to eq ['United States']
|
20
|
+
end
|
21
|
+
|
22
|
+
subject = Clin::LineReader::Readline.new(shell, 'Where are you from? ',
|
23
|
+
autocomplete: countries)
|
24
|
+
subject.readline
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'default back to the basic when echo is false' do
|
28
|
+
expect(shell.out).to receive(:print).with('Where are you from? ')
|
29
|
+
noecho_stdin = double('noecho_stdin')
|
30
|
+
expect(noecho_stdin).to receive(:gets).and_return('Asgard')
|
31
|
+
expect(shell.in).to receive(:noecho).and_yield(noecho_stdin)
|
32
|
+
subject = Clin::LineReader::Readline.new(shell, 'Where are you from? ', echo: false)
|
33
|
+
expect(subject.readline).to eq('Asgard')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.available?' do
|
38
|
+
it 'returns is available by default' do
|
39
|
+
expect(Clin::LineReader::Readline).to be_available
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns not available if disable ' do
|
43
|
+
allow(Clin).to receive(:use_readline?).and_return(false)
|
44
|
+
expect(Clin::LineReader::Readline).not_to be_available
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#autocomplete?' do
|
49
|
+
it 'return true if defined' do
|
50
|
+
subject = Clin::LineReader::Readline.new(shell, '> ', autocomplete: [''])
|
51
|
+
expect(subject.send(:autocomplete?)).to be_truthy
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'return false if false' do
|
55
|
+
subject = Clin::LineReader::Readline.new(shell, '> ', autocomplete: false)
|
56
|
+
expect(subject.send(:autocomplete?)).to be_falsey
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'return false if not defined' do
|
60
|
+
subject = Clin::LineReader::Readline.new(shell, '> ')
|
61
|
+
expect(subject.send(:autocomplete?)).to be_falsey
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Clin::LineReader do
|
4
|
+
let(:shell) { Clin::Shell.new }
|
5
|
+
it 'use readline when use_readline? is true' do
|
6
|
+
expect_any_instance_of(Clin::LineReader::Readline).to receive(:readline)
|
7
|
+
expect_any_instance_of(Clin::LineReader::Basic).not_to receive(:readline)
|
8
|
+
Clin::LineReader.scan(shell, '> ')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'use basic when use_readline? is false' do
|
12
|
+
allow(Clin).to receive(:use_readline?).and_return(false)
|
13
|
+
expect_any_instance_of(Clin::LineReader::Readline).not_to receive(:readline)
|
14
|
+
expect_any_instance_of(Clin::LineReader::Basic).to receive(:readline)
|
15
|
+
Clin::LineReader.scan(shell, '> ')
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
RSpec.describe Clin::OptionParser do
|
4
|
+
describe '#handle_unknown_option' do
|
5
|
+
let(:name) { :verbose }
|
6
|
+
let(:value) { Faker::Lorem.word }
|
7
|
+
|
8
|
+
context 'when command does not allow unknown options' do
|
9
|
+
let(:command) { double(:command, skip_options?: false) }
|
10
|
+
subject { Clin::OptionParser.new(command, []) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
subject.handle_unknown_option(name, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
it { expect(subject.errors.size).to be 1 }
|
17
|
+
it { expect(subject.errors.first).to be_a Clin::UnknownOptionError }
|
18
|
+
it { expect(subject.skipped_options).to be_empty }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when command allow unknown options and value is given' do
|
22
|
+
let(:command) { double(:command, skip_options?: true) }
|
23
|
+
subject { Clin::OptionParser.new(command, []) }
|
24
|
+
|
25
|
+
before do
|
26
|
+
subject.handle_unknown_option(name, value)
|
27
|
+
end
|
28
|
+
|
29
|
+
it { expect(subject.skipped_options.size).to eq 2 }
|
30
|
+
it { expect(subject.skipped_options).to eq [name, value] }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when command allow unknown options and value is nil' do
|
34
|
+
let(:command) { double(:command, skip_options?: true) }
|
35
|
+
subject { Clin::OptionParser.new(command, []) }
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(subject).to receive(:complete).and_return(value)
|
39
|
+
subject.handle_unknown_option(name, nil)
|
40
|
+
end
|
41
|
+
|
42
|
+
it { expect(subject.skipped_options.size).to eq 2 }
|
43
|
+
it { expect(subject.skipped_options).to eq [name, value] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#complete' do
|
48
|
+
let(:command) { double(:command) }
|
49
|
+
let(:value) { Faker::Lorem.word }
|
50
|
+
it 'keep the value when the value exists' do
|
51
|
+
parser = Clin::OptionParser.new(command, ['some'])
|
52
|
+
expect(parser.complete(value)).to eq(value)
|
53
|
+
expect(parser.instance_variable_get(:@argv)).to eq(['some'])
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'get next argument value when the value is nil' do
|
57
|
+
parser = Clin::OptionParser.new(command, [value])
|
58
|
+
expect(parser.complete(nil)).to eq(value)
|
59
|
+
expect(parser.instance_variable_get(:@argv)).to eq([])
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'return nil when value is nil and next argument is an option' do
|
63
|
+
parser = Clin::OptionParser.new(command, ['-o'])
|
64
|
+
expect(parser.complete(nil)).to be nil
|
65
|
+
expect(parser.instance_variable_get(:@argv)).to eq(['-o'])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#parse_next' do
|
70
|
+
let(:command) { double(:command) }
|
71
|
+
before do
|
72
|
+
allow(subject).to receive(:parse_short)
|
73
|
+
allow(subject).to receive(:parse_long)
|
74
|
+
subject.parse_next
|
75
|
+
end
|
76
|
+
context 'when option is a long option' do
|
77
|
+
subject { Clin::OptionParser.new(command, ['--opt=val']) }
|
78
|
+
it { expect(subject).to have_received(:parse_long).with('--opt', 'val') }
|
79
|
+
it { expect(subject).not_to have_received(:parse_short) }
|
80
|
+
it { expect(subject.instance_variable_get(:@argv)).to be_empty }
|
81
|
+
it { expect(subject.instance_variable_get(:@arguments)).to be_empty }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when option is a short option' do
|
85
|
+
subject { Clin::OptionParser.new(command, ['-oval']) }
|
86
|
+
it { expect(subject).not_to have_received(:parse_long) }
|
87
|
+
it { expect(subject).to have_received(:parse_short).with('-o', 'val') }
|
88
|
+
it { expect(subject.instance_variable_get(:@argv)).to be_empty }
|
89
|
+
it { expect(subject.instance_variable_get(:@arguments)).to be_empty }
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when option is not an option' do
|
93
|
+
subject { Clin::OptionParser.new(command, ['not opt']) }
|
94
|
+
it { expect(subject).not_to have_received(:parse_short) }
|
95
|
+
it { expect(subject).not_to have_received(:parse_long) }
|
96
|
+
it { expect(subject.instance_variable_get(:@argv)).to be_empty }
|
97
|
+
it { expect(subject.instance_variable_get(:@arguments)).to eq(['not opt']) }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#parse' do
|
102
|
+
let(:command) { double(:command) }
|
103
|
+
subject { Clin::OptionParser.new(command, []) }
|
104
|
+
|
105
|
+
it 'call parse_next until it return false' do
|
106
|
+
expect(subject).to receive(:parse_next).and_return(true, true, false).exactly(3).times
|
107
|
+
subject.parse
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#parse_option' do
|
112
|
+
let(:command) { double(:command) }
|
113
|
+
subject { Clin::OptionParser.new(command, []) }
|
114
|
+
|
115
|
+
|
116
|
+
before do
|
117
|
+
allow(subject).to receive(:handle_unknown_option)
|
118
|
+
allow(subject).to receive(:parse_flag_option)
|
119
|
+
subject.parse_option(option, '-o', value, true)
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when option is nil' do
|
123
|
+
let(:value) { 'val' }
|
124
|
+
let(:option) { nil }
|
125
|
+
it { expect(subject).to have_received(:handle_unknown_option) }
|
126
|
+
it { expect(subject).not_to have_received(:parse_flag_option) }
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when option is a flag' do
|
130
|
+
let(:value) { 'val' }
|
131
|
+
let(:option) { double(:option, flag?: true) }
|
132
|
+
it { expect(subject).not_to have_received(:handle_unknown_option) }
|
133
|
+
it { expect(subject).to have_received(:parse_flag_option) }
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'when value is nil and argument is required' do
|
137
|
+
let(:value) { nil }
|
138
|
+
let(:option) { double(:option, flag?: false, argument_optional?: false) }
|
139
|
+
|
140
|
+
it { expect(subject).not_to have_received(:handle_unknown_option) }
|
141
|
+
it { expect(subject).not_to have_received(:parse_flag_option) }
|
142
|
+
it { expect(subject.errors.first).to be_a(Clin::MissingOptionArgumentError) }
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when argument is optional or value is defined' do
|
146
|
+
let(:value) { nil }
|
147
|
+
let(:option) { double(:option, flag?: false, argument_optional?: true, trigger: true) }
|
148
|
+
|
149
|
+
it { expect(subject).not_to have_received(:handle_unknown_option) }
|
150
|
+
it { expect(subject).not_to have_received(:parse_flag_option) }
|
151
|
+
it { expect(subject.errors).to be_empty }
|
152
|
+
it { expect(option).to have_received(:trigger).with(subject, subject.options, true) }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '#parse_flag_option' do
|
157
|
+
let(:command) { double(:command) }
|
158
|
+
subject { Clin::OptionParser.new(command, []) }
|
159
|
+
let(:option) { double(:option, flag?: true, trigger: true) }
|
160
|
+
|
161
|
+
before do
|
162
|
+
allow(subject).to receive(:parse_compact_flag_options)
|
163
|
+
subject.parse_flag_option(option, value, short?)
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when value is nil' do
|
167
|
+
let(:value) { nil }
|
168
|
+
let(:short?) { false }
|
169
|
+
it { expect(option).to have_received(:trigger).with(subject, subject.options, true) }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when value exist and name is long' do
|
173
|
+
let(:value) { 'val' }
|
174
|
+
let(:short?) { false }
|
175
|
+
it { expect(option).not_to have_received(:trigger) }
|
176
|
+
it { expect(subject.errors.first).to be_a(Clin::OptionUnexpectedArgumentError) }
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when value exist and name is short' do
|
180
|
+
let(:value) { 'abc' }
|
181
|
+
let(:short?) { true }
|
182
|
+
it { expect(option).to have_received(:trigger).with(subject, subject.options, true) }
|
183
|
+
it { expect(subject).to have_received(:parse_compact_flag_options) }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe '#parse_compact_flag_options' do
|
188
|
+
let(:command) { double(:command) }
|
189
|
+
subject { Clin::OptionParser.new(command, []) }
|
190
|
+
|
191
|
+
let (:option1) { double(:option, flag?: true) }
|
192
|
+
let (:option2) { double(:option, flag?: true) }
|
193
|
+
let (:invalid_option) { double(:option, flag?: false) }
|
194
|
+
|
195
|
+
before do
|
196
|
+
allow(subject).to receive(:parse_flag_option)
|
197
|
+
allow(command).to receive(:find_option_by).and_return(*options)
|
198
|
+
subject.parse_compact_flag_options(options_arg)
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'when options are only valid flag options' do
|
202
|
+
let(:options_arg) { 'ab' }
|
203
|
+
let(:options) { [option1, option2] }
|
204
|
+
|
205
|
+
it { expect(subject).to have_received(:parse_flag_option).twice }
|
206
|
+
it { expect(subject.errors).to be_empty }
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'when 1 option is not a flag' do
|
210
|
+
let(:options_arg) { 'abc' }
|
211
|
+
let(:options) { [option1, invalid_option, option2] }
|
212
|
+
|
213
|
+
it { expect(subject).to have_received(:parse_flag_option).once }
|
214
|
+
it { expect(subject.errors.first).to be_a Clin::OptionError }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/spec/clin/option_spec.rb
CHANGED
@@ -19,7 +19,7 @@ RSpec.describe Clin::Option do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
describe '#
|
22
|
+
describe '#trigger' do
|
23
23
|
let(:out) { Hash.new }
|
24
24
|
let(:opts) { double(:opts) }
|
25
25
|
let(:value) { 'some' }
|
@@ -32,20 +32,18 @@ RSpec.describe Clin::Option do
|
|
32
32
|
context 'when initializing with name' do
|
33
33
|
subject { Clin::Option.new(:my_option, 'This is my option!') }
|
34
34
|
before do
|
35
|
-
subject.
|
35
|
+
subject.trigger(opts, out, value)
|
36
36
|
end
|
37
|
-
it { expect(opts).to have_received(:on).once }
|
38
37
|
it { expect(out[:my_option]).to eq(value) }
|
39
38
|
end
|
40
39
|
|
41
40
|
context 'when initializing with block' do
|
42
|
-
let(:block) { proc { |_opts, out, value| out[:some] = value } }
|
41
|
+
let(:block) { proc { |_opts, out, value| out[:some] = "new val #{value}" } }
|
43
42
|
subject { Clin::Option.new(:my_option, 'This is my option!', &block) }
|
44
43
|
before do
|
45
|
-
subject.
|
44
|
+
subject.trigger(opts, out, value)
|
46
45
|
end
|
47
|
-
it { expect(
|
48
|
-
it { expect(out[:some]).to eq(value) }
|
46
|
+
it { expect(out[:some]).to eq("new val #{value}") }
|
49
47
|
|
50
48
|
end
|
51
49
|
end
|
@@ -51,4 +51,34 @@ RSpec.describe Clin::ShellInteraction::Choose do
|
|
51
51
|
expect(subject.run('Where are you from?', countries)).to eq('france')
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
describe '#choice_help' do
|
56
|
+
def same?(value, actual)
|
57
|
+
expect(value.to_s.split("\n").map(&:rstrip)).to eq(actual.to_s.split("\n").map(&:rstrip))
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:choices) { {yes: 'You want it!', no: "No you don't!", yeeahno: "I can't make up my mind!"} }
|
61
|
+
it 'get help without initials' do
|
62
|
+
help = subject.choice_help(choices).to_s
|
63
|
+
expected = <<help
|
64
|
+
Choose from:
|
65
|
+
yes You want it!
|
66
|
+
no No you don't!
|
67
|
+
yeeahno I can't make up my mind!
|
68
|
+
help
|
69
|
+
same?(help, expected)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'get help with initials' do
|
73
|
+
help = subject.choice_help(choices, allow_initials: true).to_s
|
74
|
+
expected = <<help
|
75
|
+
Choose from:
|
76
|
+
y - yes You want it!
|
77
|
+
n - no No you don't!
|
78
|
+
yeeahno I can't make up my mind!
|
79
|
+
help
|
80
|
+
same?(help, expected)
|
81
|
+
# expect(help).to eq(expected)
|
82
|
+
end
|
83
|
+
end
|
54
84
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
RSpec.describe Clin::ShellInteraction::FileConflict do
|
4
|
+
let(:shell) { Clin::Shell.new }
|
5
|
+
subject { Clin::ShellInteraction::FileConflict.new(shell) }
|
6
|
+
|
7
|
+
describe 'show_diff' do
|
8
|
+
it 'open a tmp file' do
|
9
|
+
file = double(:file, path: 'tmp/filename.txt')
|
10
|
+
expect(file).to receive(:write).with('new_content')
|
11
|
+
expect(file).to receive(:rewind)
|
12
|
+
expect(Tempfile).to receive(:open).with('filename.txt').and_yield(file)
|
13
|
+
expect_any_instance_of(Kernel)
|
14
|
+
.to receive(:system).with('diff -u "path/filename.txt" "tmp/filename.txt"')
|
15
|
+
subject.send(:show_diff, 'path/filename.txt', 'new_content')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'clin'
|
2
|
+
|
3
|
+
RSpec.describe Clin::ShellInteraction::Select do
|
4
|
+
let(:shell) { Clin::Shell.new }
|
5
|
+
subject { Clin::ShellInteraction::Select.new(shell) }
|
6
|
+
|
7
|
+
def expects_scan(*outputs)
|
8
|
+
expect(shell).to receive(:scan).with('>', any_args)
|
9
|
+
.and_return(*outputs).exactly(outputs.size).times
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#run' do
|
13
|
+
suppress_puts
|
14
|
+
let(:choices) { %w(usa france germany italy) }
|
15
|
+
|
16
|
+
it 'ask for a choice and return the user reply' do
|
17
|
+
expects_scan('france')
|
18
|
+
expect(subject.run('Where are you from?', choices)).to eq('france')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'ask for a choice and return the default' do
|
22
|
+
expects_scan('')
|
23
|
+
expect(subject).to receive(:choice_help).once
|
24
|
+
expect(subject.run('Where are you from?', choices, default: 'germany')).to eq('germany')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'get the value using index' do
|
28
|
+
expects_scan('4')
|
29
|
+
expect(subject).to receive(:choice_help).once
|
30
|
+
expect(subject.run('Where are you from?', choices)).to eq('italy')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'keep asking until the answer is valid' do
|
34
|
+
expects_scan('spain', 'russia', 'france')
|
35
|
+
expect(subject).to receive(:choice_help).exactly(3).times
|
36
|
+
expect(subject.run('Where are you from?', choices)).to eq('france')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'keep asking until the answer is valid' do
|
40
|
+
expects_scan('0', '10', '2')
|
41
|
+
expect(subject).to receive(:choice_help).exactly(3).times
|
42
|
+
expect(subject.run('Where are you from?', choices)).to eq('france')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#get_choice' do
|
47
|
+
let(:choices) { ['Choice A', 'Choice B', 'Choice C', 'Choice D'] }
|
48
|
+
|
49
|
+
it 'get choice with its value' do
|
50
|
+
expect(subject.get_choice(choices, 'Choice B', 1)).to eq('Choice B')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'get choice with its index' do
|
54
|
+
expect(subject.get_choice(choices, '3', 1)).to eq('Choice C')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'return nil when value is less' do
|
58
|
+
expect(subject.get_choice(choices, '0', 1)).to be nil
|
59
|
+
end
|
60
|
+
it 'return nil when value is less' do
|
61
|
+
expect(subject.get_choice(choices, '5', 1)).to be nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#choice_help' do
|
66
|
+
def same?(value, actual)
|
67
|
+
expect(value.to_s.split("\n").map(&:rstrip)).to eq(actual.to_s.split("\n").map(&:rstrip))
|
68
|
+
end
|
69
|
+
|
70
|
+
let(:choices) { ['Choice A', 'Choice B', 'Choice C'] }
|
71
|
+
let(:choices_desc) { {'Choice A' => 'This is choice A',
|
72
|
+
'Choice Long' => 'This is choice B',
|
73
|
+
'Choice C' => 'This is choice C'} }
|
74
|
+
it 'get help without description' do
|
75
|
+
help = subject.choice_help(choices, 1).to_s
|
76
|
+
expected = <<help
|
77
|
+
1. Choice A
|
78
|
+
2. Choice B
|
79
|
+
3. Choice C
|
80
|
+
help
|
81
|
+
# same?(help, expected)
|
82
|
+
expect(help).to eq(expected)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'get help with description' do
|
86
|
+
help = subject.choice_help(choices_desc, 0).to_s
|
87
|
+
expected = <<help
|
88
|
+
0. Choice A, This is choice A
|
89
|
+
1. Choice Long, This is choice B
|
90
|
+
2. Choice C, This is choice C
|
91
|
+
help
|
92
|
+
# same?(help, expected)
|
93
|
+
expect(help).to eq(expected)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/spec/clin/shell_spec.rb
CHANGED
@@ -5,6 +5,22 @@ RSpec.describe Clin::Shell do
|
|
5
5
|
expect(subject).to receive(:scan).with(message, any_args).and_return(*outputs).exactly(outputs.size).times
|
6
6
|
end
|
7
7
|
|
8
|
+
describe '#say' do
|
9
|
+
it 'call text#line' do
|
10
|
+
expect(subject.out).to receive(:puts).with(' Lorem Ipsum')
|
11
|
+
subject.say('Lorem Ipsum', indent: 2)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#indent' do
|
16
|
+
it 'call text#line' do
|
17
|
+
expect(subject.out).to receive(:puts).with(' **Lorem Ipsum')
|
18
|
+
subject.indent 2 do
|
19
|
+
subject.say('Lorem Ipsum', indent: '**')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
8
24
|
describe '#ask' do
|
9
25
|
it 'ask for a question and return user reply' do
|
10
26
|
expects_scan('What is your name?', 'Smith')
|
@@ -17,6 +33,14 @@ RSpec.describe Clin::Shell do
|
|
17
33
|
end
|
18
34
|
end
|
19
35
|
|
36
|
+
describe '#password' do
|
37
|
+
it 'call ask with echo and add_to_history false' do
|
38
|
+
expect(subject).to receive(:ask)
|
39
|
+
.with('Password?', default: 'lorem', echo: false, add_to_history: false)
|
40
|
+
subject.password('Password?', default: 'lorem')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
20
44
|
describe '#choose' do
|
21
45
|
let(:countries) { %w(usa france germany italy) }
|
22
46
|
|
@@ -36,6 +60,16 @@ RSpec.describe Clin::Shell do
|
|
36
60
|
end
|
37
61
|
end
|
38
62
|
|
63
|
+
describe '#select' do
|
64
|
+
it 'call ask with echo and add_to_history false' do
|
65
|
+
select = double(:select_interaction)
|
66
|
+
choices = %w(France Germany Italy Spain)
|
67
|
+
expect(select).to receive(:run).with('Where are you from?', choices, default: 'France')
|
68
|
+
expect(Clin::ShellInteraction::Select).to receive(:new).and_return(select)
|
69
|
+
subject.select('Where are you from?', choices, default: 'France')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
39
73
|
describe '#yes_or_no' do
|
40
74
|
it 'asks the user and returns true if the user replies y' do
|
41
75
|
expects_scan('Is earth round? [yn]', 'y')
|
@@ -144,4 +178,12 @@ RSpec.describe Clin::Shell do
|
|
144
178
|
expect(subject.keep?('some.txt')).to be false
|
145
179
|
end
|
146
180
|
end
|
181
|
+
|
182
|
+
describe '#scan' do
|
183
|
+
it 'call LineReader.scan' do
|
184
|
+
options = {opt1: 'val1', opt2: 'val2'}
|
185
|
+
expect(Clin::LineReader).to receive(:scan).with(subject, 'Where are you from? ', options)
|
186
|
+
subject.send(:scan, 'Where are you from?', options)
|
187
|
+
end
|
188
|
+
end
|
147
189
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Clin::Text::TableCell do
|
4
|
+
def table_double(options)
|
5
|
+
double(:table, options.reverse_merge(column_length: {}, update_column_length: true))
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#length' do
|
9
|
+
let(:index) { 1 }
|
10
|
+
let(:table) { table_double(column_length: [1, 2, 3]) }
|
11
|
+
subject { Clin::Text::TableCell.new(table, index, 'val') }
|
12
|
+
|
13
|
+
it 'Get the length of the corresponding column' do
|
14
|
+
expect(subject.length).to eq(2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#align' do
|
19
|
+
let(:index) { 1 }
|
20
|
+
let(:table) { table_double(align: align) }
|
21
|
+
subject { Clin::Text::TableCell.new(table, 1, 'val') }
|
22
|
+
|
23
|
+
context 'when align was set using a symbol' do
|
24
|
+
let(:align) { :center }
|
25
|
+
|
26
|
+
it 'return the global align setting' do
|
27
|
+
expect(subject.align).to eq(align)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when align was set column specific' do
|
32
|
+
let(:align) { [:center, :right, :center] }
|
33
|
+
|
34
|
+
it 'return the column specific setting' do
|
35
|
+
expect(subject.align).to eq(:right)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when align is in wrong format' do
|
40
|
+
let(:align) { {left: true} }
|
41
|
+
|
42
|
+
it 'return the column specific setting' do
|
43
|
+
expect { subject.align }.to raise_error(Clin::Error)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#to_s' do
|
49
|
+
let(:table) { table_double(column_length: {}) }
|
50
|
+
subject { Clin::Text::TableCell.new(table, 1, 'value') }
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(subject).to receive(:length).and_return(9)
|
54
|
+
allow(subject).to receive(:align).and_return(align)
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when align is left' do
|
58
|
+
let(:align) { :left }
|
59
|
+
it { expect(subject.to_s).to eq('value ') }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when align is right' do
|
63
|
+
let(:align) { :right }
|
64
|
+
it { expect(subject.to_s).to eq(' value') }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when align is center' do
|
68
|
+
let(:align) { :center }
|
69
|
+
it { expect(subject.to_s).to eq(' value ') }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|