rainforest-cli 1.6.5 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +8 -0
  4. data/README.md +42 -7
  5. data/bin/rainforest +1 -1
  6. data/lib/{rainforest/cli.rb → rainforest_cli.rb} +23 -15
  7. data/lib/{rainforest/cli → rainforest_cli}/constants.rb +0 -0
  8. data/lib/{rainforest/cli → rainforest_cli}/csv_importer.rb +0 -0
  9. data/lib/{rainforest/cli → rainforest_cli}/deleter.rb +0 -0
  10. data/lib/{rainforest/cli → rainforest_cli}/exporter.rb +0 -0
  11. data/lib/{rainforest/cli → rainforest_cli}/git_trigger.rb +0 -0
  12. data/lib/{rainforest/cli → rainforest_cli}/http_client.rb +5 -4
  13. data/lib/rainforest_cli/junit_outputter.rb +69 -0
  14. data/lib/{rainforest/cli → rainforest_cli}/options.rb +40 -1
  15. data/lib/{rainforest/cli → rainforest_cli}/remote_tests.rb +0 -0
  16. data/lib/rainforest_cli/reporter.rb +64 -0
  17. data/lib/{rainforest/cli → rainforest_cli}/resources.rb +0 -0
  18. data/lib/{rainforest/cli → rainforest_cli}/runner.rb +20 -6
  19. data/lib/{rainforest/cli → rainforest_cli}/test_files.rb +0 -0
  20. data/lib/{rainforest/cli → rainforest_cli}/test_parser.rb +4 -65
  21. data/lib/rainforest_cli/test_parser/embedded_test.rb +14 -0
  22. data/lib/rainforest_cli/test_parser/step.rb +24 -0
  23. data/lib/rainforest_cli/test_parser/test.rb +40 -0
  24. data/lib/{rainforest/cli → rainforest_cli}/uploader.rb +32 -27
  25. data/lib/rainforest_cli/uploader/multi_form_post_request.rb +50 -0
  26. data/lib/rainforest_cli/uploader/uploadable_parser.rb +143 -0
  27. data/lib/{rainforest/cli → rainforest_cli}/validator.rb +0 -0
  28. data/lib/{rainforest/cli → rainforest_cli}/version.rb +1 -1
  29. data/rainforest-cli.gemspec +3 -1
  30. data/spec/fixtures/failed_test_response.json +29 -0
  31. data/spec/fixtures/runs_response.json +275 -0
  32. data/spec/fixtures/tests_response.json +130 -0
  33. data/spec/{csv_importer_spec.rb → rainforest_cli/csv_importer_spec.rb} +7 -14
  34. data/spec/{deleter_spec.rb → rainforest_cli/deleter_spec.rb} +0 -0
  35. data/spec/{exporter_spec.rb → rainforest_cli/exporter_spec.rb} +0 -0
  36. data/spec/{git_trigger_spec.rb → rainforest_cli/git_trigger_spec.rb} +0 -0
  37. data/spec/{http_client_spec.rb → rainforest_cli/http_client_spec.rb} +23 -0
  38. data/spec/rainforest_cli/junit_outputter_spec.rb +32 -0
  39. data/spec/{options_spec.rb → rainforest_cli/options_spec.rb} +33 -0
  40. data/spec/{remote_tests_spec.rb → rainforest_cli/remote_tests_spec.rb} +0 -0
  41. data/spec/rainforest_cli/reporter_spec.rb +51 -0
  42. data/spec/{resources_spec.rb → rainforest_cli/resources_spec.rb} +0 -0
  43. data/spec/{runner_spec.rb → rainforest_cli/runner_spec.rb} +0 -0
  44. data/spec/{test_files_spec.rb → rainforest_cli/test_files_spec.rb} +1 -1
  45. data/spec/rainforest_cli/test_parser/step_spec.rb +53 -0
  46. data/spec/rainforest_cli/test_parser_spec.rb +15 -0
  47. data/spec/rainforest_cli/uploader/uploadable_parser_spec.rb +84 -0
  48. data/spec/{uploader_spec.rb → rainforest_cli/uploader_spec.rb} +2 -2
  49. data/spec/{validator_spec.rb → rainforest_cli/validator_spec.rb} +6 -7
  50. data/spec/{cli_spec.rb → rainforest_cli_spec.rb} +5 -5
  51. data/spec/spec_helper.rb +4 -1
  52. metadata +95 -47
  53. data/.rvmrc +0 -1
  54. data/spec/test_parser_spec.rb +0 -95
@@ -1,22 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  describe RainforestCli::CSVImporter do
3
- let(:csv_file) { "#{File.dirname(__FILE__)}/fixtures/variables.txt" }
3
+ let(:csv_file) { "#{File.dirname(__FILE__)}/../fixtures/variables.txt" }
4
4
 
5
5
  describe '.import' do
6
6
  subject { described_class.new('variables', csv_file, 'abc123') }
7
-
8
- before do
9
- # suppress output in terminal
10
- allow_any_instance_of(described_class).to receive(:print)
11
- allow_any_instance_of(described_class).to receive(:puts)
12
- end
13
-
14
7
  let(:columns) { %w(email pass) }
15
8
 
16
9
  let(:success_response) do
17
10
  {
18
11
  'id' => 12345,
19
- 'columns' => columns.each_with_index.map { |col, i| { 'id' => i, 'name' => col } }
12
+ 'columns' => columns.each_with_index.map { |col, i| { 'id' => i, 'name' => col } },
20
13
  }
21
14
  end
22
15
 
@@ -25,7 +18,7 @@ describe RainforestCli::CSVImporter do
25
18
  .with('/generators', {
26
19
  name: 'variables',
27
20
  description: 'variables',
28
- columns: columns.map {|col| { name: col } }
21
+ columns: columns.map {|col| { name: col } },
29
22
  })
30
23
  .and_return success_response
31
24
 
@@ -33,16 +26,16 @@ describe RainforestCli::CSVImporter do
33
26
  .with('/generators/12345/rows', {
34
27
  data: {
35
28
  0 => 'russ@rainforestqa.com',
36
- 1 => 'abc123'
37
- }
29
+ 1 => 'abc123',
30
+ },
38
31
  }).and_return({})
39
32
 
40
33
  expect_any_instance_of(RainforestCli::HttpClient).to receive(:post)
41
34
  .with('/generators/12345/rows', {
42
35
  data: {
43
36
  0 => 'bob@example.com',
44
- 1 => 'hunter2'
45
- }
37
+ 1 => 'hunter2',
38
+ },
46
39
  }).and_return({})
47
40
 
48
41
  subject.import
@@ -53,5 +53,28 @@ describe RainforestCli::HttpClient do
53
53
  end
54
54
  end
55
55
  end
56
+
57
+ describe 'non 200 codes' do
58
+ context '404 not found'do
59
+ let(:url) { 'http://some.url.com' }
60
+ let(:response) { instance_double('HTTParty::Response', code: 404, body: {'error'=>'some error', 'type'=>'some type'}.to_json) }
61
+ subject { described_class.new({ token: 'foo' }).get(url, {}) }
62
+
63
+ before do
64
+ allow(HTTParty).to receive(:get).and_raise(SocketError)
65
+ end
66
+
67
+ it 'gets an error 404 and prints the error' do
68
+ expect(HTTParty).to receive(:get).and_return(response)
69
+ expect_any_instance_of(Logger).to receive(:warn).with('Status Code: 404, {"error":"some error","type":"some type"}')
70
+ expect(subject)
71
+ end
72
+
73
+ it 'returns nil' do
74
+ expect(HTTParty).to receive(:get).and_return(response)
75
+ expect(subject).to eq(nil)
76
+ end
77
+ end
78
+ end
56
79
  end
57
80
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+ require 'stringio'
4
+
5
+ describe RainforestCli::JunitOutputter do
6
+ let(:spec_folder) { File.expand_path(File.dirname(__FILE__) + '/..') }
7
+ let(:runs_json_results) { JSON.parse(File.read("#{spec_folder}/fixtures/runs_response.json")) }
8
+ let(:tests_json_results) { JSON.parse(File.read("#{spec_folder}/fixtures/tests_response.json")) }
9
+ let(:failed_test_json) { JSON.parse(File.read("#{spec_folder}/fixtures/failed_test_response.json")) }
10
+ let(:test_io) { StringIO.new }
11
+
12
+ describe '.build_test_suite' do
13
+
14
+ subject { described_class.new('abc123', runs_json_results, tests_json_results) }
15
+
16
+ before do
17
+ allow(subject.client).to receive(:get).and_return(failed_test_json)
18
+ end
19
+
20
+ context 'With a valid response' do
21
+ it 'Parses the response' do
22
+ subject.parse
23
+ subject.output(test_io)
24
+
25
+ expect(test_io.string).to include('name="Test Description"')
26
+ expect(test_io.string).to include('failures="2"')
27
+ expect(test_io.string).to include('This feedback should appear')
28
+ expect(test_io.string).not_to include("This feedback shouldn't "appear")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -80,6 +80,12 @@ describe RainforestCli::OptionParser do
80
80
  its(:foreground?) { is_expected.to eq(true) }
81
81
  end
82
82
 
83
+ context 'it parses the --wait flag' do
84
+ let(:args) { ['run', '--wait', '12345'] }
85
+ its(:wait?) { is_expected.to be true }
86
+ its(:run_id) { is_expected.to eq 12345 }
87
+ end
88
+
83
89
  context 'it parses the api token' do
84
90
  let(:args) { ['run', '--token', 'abc', 'all'] }
85
91
  its(:token) { should == 'abc'}
@@ -172,5 +178,32 @@ describe RainforestCli::OptionParser do
172
178
  it { raises_a_validation_exception }
173
179
  end
174
180
  end
181
+
182
+ context 'with junit output but not in foreground mode' do
183
+ let(:args) { %w(run --token foo --junit-file some_file.xml) }
184
+ it { raises_a_validation_exception }
185
+ end
186
+
187
+ context 'with junit output and in foreground mode' do
188
+ let(:args) { %w(--token foo --junit-file some_file.xml --fg) }
189
+ it { does_not_raise_a_validation_exception }
190
+ end
191
+
192
+ context 'valdiating the report command' do
193
+ context 'with a valid junit and run_id' do
194
+ let(:args) { ['report', '--junit-file', 'somefile.xml', '--run-id', '12345', '--token', 'foo'] }
195
+ it {does_not_raise_a_validation_exception }
196
+ end
197
+
198
+ context 'with a junit but no run_id' do
199
+ let(:args) { ['report', '--junit-file', 'somefile.xml', '--token', 'foo'] }
200
+ it { raises_a_validation_exception }
201
+ end
202
+
203
+ context 'with a run_id but no junit' do
204
+ let(:args) { ['report', '--run-id', '12345', '--token', 'foo'] }
205
+ end
206
+ end
207
+
175
208
  end
176
209
  end
@@ -0,0 +1,51 @@
1
+
2
+
3
+ describe RainforestCli::Reporter do
4
+ let(:args) { %w(report --token abc123 --run-id 12345 --junit-file somefile.xml) }
5
+ let(:options) { RainforestCli::OptionParser.new(args) }
6
+
7
+ subject { described_class.new(options) }
8
+
9
+ describe '#report' do
10
+ context 'on /runs/{run_id}.json API error' do
11
+ before do
12
+ allow(subject.client).to receive(:get).with('/runs/12345.json').and_return({'error'=>'Some API error'})
13
+ end
14
+
15
+ it 'errors out and exits' do
16
+ expect_any_instance_of(Logger).to receive(:fatal).with('Error retrieving results for your run: Some API error')
17
+ expect do
18
+ subject.report
19
+ end.to raise_error(SystemExit) { |error|
20
+ expect(error.status).to eq 1
21
+ }
22
+ end
23
+
24
+ end
25
+
26
+ context 'on /runs/{run_id}/tests.json API error' do
27
+ before do
28
+ allow(subject.client).to receive(:get).with('/runs/12345.json').and_return({'total_tests'=>'1'})
29
+ allow(subject.client).to receive(:get).with('/runs/12345/tests.json?page_size=1').and_return({'error'=>'Some API error'})
30
+ end
31
+
32
+ it 'errors and exits' do
33
+ expect_any_instance_of(Logger).to receive(:fatal).with('Error retrieving test details for your run: Some API error')
34
+ expect do
35
+ subject.report
36
+ end.to raise_error(SystemExit) { |error|
37
+ expect(error.status).to eq 1
38
+ }
39
+ end
40
+
41
+ end
42
+
43
+ context 'with working API calls creates JunitOutputter' do
44
+ before do
45
+ allow(subject.client).to receive(:get).with('/runs/12345.json').and_return()
46
+ allow(subject.client).to receive(:get).with('/runs/12345/tests.json?page_size=1').and_return()
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -4,7 +4,7 @@ describe RainforestCli::TestFiles do
4
4
  subject { described_class.new(options) }
5
5
 
6
6
  describe '#test_data' do
7
- let(:test_directory) { File.dirname(__FILE__) + '/rainforest-example' }
7
+ let(:test_directory) { File.dirname(__FILE__) + '/../rainforest-example' }
8
8
  let(:options) { instance_double('RainforestCli::Options', test_folder: test_directory, command: nil) }
9
9
 
10
10
  let(:rfml_test) { subject.test_data.first }
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ describe RainforestCli::TestParser::Step do
3
+ subject { described_class.new }
4
+ let(:screenshot_string) do
5
+ 'Picture 1: {{ file.screenshot(./foo) }}. Picture 2: {{file.screenshot(bar/baz) }}'
6
+ end
7
+
8
+ let(:download_string) do
9
+ 'Download 1: {{ file.download(./foo) }}. Download 2: {{file.download(bar/baz) }}'
10
+ end
11
+
12
+ shared_examples 'a method that detects step variables' do |att|
13
+ it 'correctly detects a screenshot step variable' do
14
+ subject[att] = screenshot_string
15
+ expect(subject.send(:"uploadable_in_#{att}"))
16
+ .to eq([['screenshot', './foo'], ['screenshot', 'bar/baz']])
17
+ end
18
+
19
+ it 'correctly detects the download step variable' do
20
+ subject[att] = download_string
21
+ expect(subject.send(:"uploadable_in_#{att}"))
22
+ .to eq([['download', './foo'], ['download', 'bar/baz']])
23
+ end
24
+ end
25
+
26
+ describe '#uploadable_in_action' do
27
+ it_behaves_like 'a method that detects step variables', :action
28
+ end
29
+
30
+ describe '#uploadable_in_response' do
31
+ it_behaves_like 'a method that detects step variables', :response
32
+ end
33
+
34
+ describe '#has_uploadable_files?' do
35
+ let(:action) { 'Regular action' }
36
+ let(:response) { 'Regular response' }
37
+ subject { described_class.new(action, response).has_uploadable_files? }
38
+
39
+ context 'with no uploadables' do
40
+ it { is_expected.to be(false) }
41
+ end
42
+
43
+ context 'uploadable in action' do
44
+ let(:action) { 'Action with uploadable {{ file.download(/foo) }}' }
45
+ it { is_expected.to be(true) }
46
+ end
47
+
48
+ context 'uploadable in response' do
49
+ let(:response) { 'Response with uploadable {{ file.download(/foo) }}' }
50
+ it { is_expected.to be(true) }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ describe RainforestCli::TestParser do
3
+ describe RainforestCli::TestParser::Parser do
4
+ subject { described_class.new(file_name) }
5
+
6
+ describe '#initialize' do
7
+ let(:file_name) { './spec/rainforest-example/example_test.rfml' }
8
+
9
+ it 'expands the file name path' do
10
+ test = subject.instance_variable_get(:@test)
11
+ expect(test.file_name).to eq(File.expand_path(file_name))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+ describe RainforestCli::Uploader::UploadableParser do
3
+ let(:rfml_test) { instance_double('RainforestCli::TestParser::Test', file_name: 'rfml_test_filename.rfml') }
4
+ let(:test_id) { 12345 }
5
+ let(:uploaded_files) { [] }
6
+ subject { described_class.new(rfml_test, test_id, uploaded_files) }
7
+
8
+ describe '#replace_paths_in_text' do
9
+ let(:file) { instance_double('File') }
10
+ let(:file_path) { 'path/to/my/file.ext' }
11
+ let(:file_id) { 9876 }
12
+ let(:file_sig) { 'abcdef' }
13
+ let(:aws_info) do
14
+ { 'file_id' => file_id, 'file_signature' => "#{file_sig}xyz123" }
15
+ end
16
+
17
+ before do
18
+ allow(subject).to receive(:test_directory).and_return('my/test/directory')
19
+ allow(File).to receive(:exist?).and_return(true)
20
+ allow(File).to receive(:open).and_return(file)
21
+ allow(subject).to receive(:get_aws_upload_info).with(file).and_return(aws_info)
22
+ end
23
+
24
+ context 'screenshot' do
25
+ let(:original_text) { "My screenshot is {{ file.screenshot(#{file_path}) }}" }
26
+ let(:match) { ['screenshot', file_path] }
27
+ let(:expected_text) { "My screenshot is {{ file.screenshot(#{file_id}, #{file_sig}) }}" }
28
+
29
+ it 'replaces the screenshot variable arguments with the correct values' do
30
+ expect(subject.replace_paths_in_text(original_text, match)).to eq(expected_text)
31
+ end
32
+ end
33
+
34
+ context 'download' do
35
+ let(:original_text) { "My download is {{ file.download(#{file_path}) }}" }
36
+ let(:match) { ['download', file_path] }
37
+ let(:file_name) { File.basename(file_path) }
38
+ let(:expected_text) { "My download is {{ file.download(#{file_id}, #{file_sig}, #{file_name}) }}" }
39
+
40
+ it 'replaces the download variable arguments with the correct values' do
41
+ expect(subject.replace_paths_in_text(original_text, match)).to eq(expected_text)
42
+ end
43
+ end
44
+
45
+ context 'file does not exist' do
46
+ let(:original_text) { "My download is {{ file.download(#{file_path}) }}" }
47
+ let(:match) { ['download', file_path] }
48
+ let(:file_name) { File.basename(file_path) }
49
+
50
+ before do
51
+ allow(File).to receive(:exist?).and_call_original # remove stub in previous before block
52
+ expect(File).to receive(:exist?).with(a_string_including(file_path)).and_return(false)
53
+ end
54
+
55
+ it 'warns the user and does not replace the variable values' do
56
+ expect_any_instance_of(Logger).to receive(:warn).with(a_string_including(rfml_test.file_name))
57
+ expect_any_instance_of(Logger).to receive(:warn).with(a_string_including(file_name))
58
+ expect(subject.replace_paths_in_text(original_text, match)).to eq(original_text)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '#upload_to_rainforest' do
64
+ let(:file_path) { 'test_file.txt' }
65
+ let(:mime_type) { 'text/plain' }
66
+ let(:size) { 4545 }
67
+ let(:file) { instance_double('File', path: file_path, size: size) }
68
+ let(:digest) { 'myDigest' }
69
+
70
+ before do
71
+ allow(subject).to receive(:file_digest).with(file).and_return(digest)
72
+ allow(MimeMagic).to receive(:by_path).with(file).and_return(mime_type)
73
+ end
74
+
75
+ it 'uploads the correct metadata to Rainforest' do
76
+ expect(RainforestCli.http_client).to receive(:post) do |url, params|
77
+ expect(url).to include(test_id.to_s)
78
+ expect(params).to include(mime_type: mime_type, size: size, name: file_path, digest: digest)
79
+ end.and_return({ 'aws_url' => 'http://aws.amazon.com/lol' })
80
+
81
+ subject.upload_to_rainforest(file)
82
+ end
83
+ end
84
+ end
@@ -11,7 +11,7 @@ describe RainforestCli::Uploader do
11
11
 
12
12
  describe '#upload' do
13
13
  context 'with new tests' do
14
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples/correct_embeds')) }
14
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples/correct_embeds') }
15
15
  let(:rf_test_double) { instance_double('Rainforest::Test', id: 123) }
16
16
 
17
17
  before do
@@ -27,7 +27,7 @@ describe RainforestCli::Uploader do
27
27
  end
28
28
 
29
29
  describe 'uploaded test object' do
30
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../rainforest-example')) }
30
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../rainforest-example') }
31
31
  let(:test_double) { instance_double('Rainforest::Test', id: 123) }
32
32
 
33
33
  before do
@@ -30,7 +30,7 @@ describe RainforestCli::Validator do
30
30
 
31
31
  context 'with parsing errors' do
32
32
  let(:notification_method) { :parsing_error_notification }
33
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples/parse_errors')) }
33
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples/parse_errors') }
34
34
 
35
35
  context 'no rfml id' do
36
36
  let(:correct_file_name) { 'no_rfml_id.rfml' }
@@ -55,7 +55,7 @@ describe RainforestCli::Validator do
55
55
 
56
56
  context 'with a incorrect embedded RFML ID' do
57
57
  let(:notification_method) { :nonexisting_embedded_id_notification }
58
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples/missing_embeds')) }
58
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples/missing_embeds') }
59
59
 
60
60
  context 'the file containing in the incorrect id' do
61
61
  let(:correct_file_name) { 'incorrect_test.rfml' }
@@ -69,7 +69,7 @@ describe RainforestCli::Validator do
69
69
  end
70
70
 
71
71
  context 'with circular embeds' do
72
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples/circular_embeds')) }
72
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples/circular_embeds') }
73
73
  let(:file_name_a) { File.join(test_directory, 'test1.rfml') }
74
74
  let(:file_name_b) { File.join(test_directory, 'test2.rfml') }
75
75
 
@@ -87,7 +87,7 @@ describe RainforestCli::Validator do
87
87
  it_behaves_like 'it detects all the correct errors'
88
88
 
89
89
  context 'when multiple tests have the same rfml_ids' do
90
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples/duplicate_rfml_ids')) }
90
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples/duplicate_rfml_ids') }
91
91
  let(:options) { instance_double('RainforestCli::Options', test_folder: test_directory, token: 'api_token', command: '') }
92
92
 
93
93
  subject { described_class.new(options) }
@@ -102,7 +102,6 @@ describe RainforestCli::Validator do
102
102
  expect { subject.validate }.to raise_error(SystemExit)
103
103
  end
104
104
 
105
-
106
105
  context 'when invalid' do
107
106
  before do
108
107
  allow(subject).to receive(:invalid?).and_return(true)
@@ -110,7 +109,7 @@ describe RainforestCli::Validator do
110
109
  it 'exits 1' do
111
110
  begin
112
111
  subject.validate
113
- fail "validate did not exit with status 1"
112
+ fail 'validate did not exit with status 1'
114
113
  rescue SystemExit => e
115
114
  expect(e.status).to eq(1)
116
115
  end
@@ -125,7 +124,7 @@ describe RainforestCli::Validator do
125
124
  it_behaves_like 'it detects all the correct errors'
126
125
 
127
126
  context 'without a token option' do
128
- let(:test_directory) { File.expand_path(File.join(__FILE__, '../validation-examples')) }
127
+ let(:test_directory) { File.expand_path(File.dirname(__FILE__) + '/../validation-examples') }
129
128
  let(:options) { instance_double('RainforestCli::Options', test_folder: test_directory, token: nil, command: '') }
130
129
  subject { described_class.new(options) }
131
130