rainforest-cli 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +5 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +8 -0
  7. data/Gemfile +7 -1
  8. data/README.md +2 -0
  9. data/Rakefile +6 -4
  10. data/circle.yml +3 -0
  11. data/lib/rainforest/cli.rb +20 -16
  12. data/lib/rainforest/cli/constants.rb +4 -0
  13. data/lib/rainforest/cli/csv_importer.rb +6 -6
  14. data/lib/rainforest/cli/git_trigger.rb +2 -1
  15. data/lib/rainforest/cli/http_client.rb +50 -13
  16. data/lib/rainforest/cli/options.rb +71 -39
  17. data/lib/rainforest/cli/remote_tests.rb +49 -0
  18. data/lib/rainforest/cli/runner.rb +19 -17
  19. data/lib/rainforest/cli/test_files.rb +32 -14
  20. data/lib/rainforest/cli/test_importer.rb +35 -155
  21. data/lib/rainforest/cli/test_parser.rb +38 -14
  22. data/lib/rainforest/cli/uploader.rb +107 -0
  23. data/lib/rainforest/cli/validator.rb +158 -0
  24. data/lib/rainforest/cli/version.rb +2 -1
  25. data/rainforest-cli.gemspec +14 -12
  26. data/spec/cli_spec.rb +84 -90
  27. data/spec/csv_importer_spec.rb +13 -8
  28. data/spec/git_trigger_spec.rb +28 -15
  29. data/spec/http_client_spec.rb +57 -0
  30. data/spec/options_spec.rb +72 -70
  31. data/spec/rainforest-example/example_test.rfml +2 -1
  32. data/spec/remote_tests_spec.rb +22 -0
  33. data/spec/runner_spec.rb +17 -16
  34. data/spec/spec_helper.rb +16 -9
  35. data/spec/test_files_spec.rb +20 -24
  36. data/spec/uploader_spec.rb +54 -0
  37. data/spec/validation-examples/circular_embeds/test1.rfml +5 -0
  38. data/spec/validation-examples/circular_embeds/test2.rfml +5 -0
  39. data/spec/validation-examples/correct_embeds/embedded_test.rfml +6 -0
  40. data/spec/validation-examples/correct_embeds/test_with_embedded.rfml +8 -0
  41. data/spec/validation-examples/missing_embeds/correct_test.rfml +8 -0
  42. data/spec/validation-examples/missing_embeds/incorrect_test.rfml +8 -0
  43. data/spec/validation-examples/parse_errors/no_parse_errors.rfml +6 -0
  44. data/spec/validation-examples/parse_errors/no_question.rfml +5 -0
  45. data/spec/validation-examples/parse_errors/no_question_mark.rfml +6 -0
  46. data/spec/validation-examples/parse_errors/no_rfml_id.rfml +5 -0
  47. data/spec/validator_spec.rb +119 -0
  48. metadata +96 -16
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module RainforestCli::TestParser
2
3
  class EmbeddedTest < Struct.new(:rfml_id)
3
4
  def type
@@ -7,6 +8,16 @@ module RainforestCli::TestParser
7
8
  def to_s
8
9
  "--> embed: #{rfml_id}"
9
10
  end
11
+
12
+ def to_element(primary_key_id)
13
+ {
14
+ type: 'test',
15
+ redirection: true,
16
+ element: {
17
+ id: primary_key_id
18
+ }
19
+ }
20
+ end
10
21
  end
11
22
 
12
23
  class Step < Struct.new(:action, :response)
@@ -17,28 +28,40 @@ module RainforestCli::TestParser
17
28
  def to_s
18
29
  "#{action} --> #{response}"
19
30
  end
20
- end
21
31
 
22
- class Error < Struct.new(:line, :reason)
23
- def to_s
24
- "Line #{line}: #{reason}"
32
+ def to_element
33
+ {
34
+ type: 'step',
35
+ redirection: true,
36
+ element: {
37
+ action: action,
38
+ response: response
39
+ }
40
+ }
25
41
  end
26
42
  end
27
43
 
28
- class Test < Struct.new(:rfml_id, :description, :title, :start_uri, :steps, :errors, :tags, :browsers)
44
+ class Test < Struct.new(:file_name, :rfml_id, :description, :title, :start_uri, :steps, :errors, :tags, :browsers)
29
45
  def embedded_ids
30
46
  steps.inject([]) { |embeds, step| step.type == :test ? embeds + [step.rfml_id] : embeds }
31
47
  end
32
48
  end
33
49
 
50
+ class Error < Struct.new(:line, :reason)
51
+ def to_s
52
+ "Line #{line}: #{reason}"
53
+ end
54
+ end
55
+
34
56
  class Parser
35
57
  attr_reader :steps, :errors, :text
36
58
 
37
- def initialize(text)
38
- @text = text.to_s
59
+ def initialize(file_name)
60
+ @text = File.read(file_name).to_s
39
61
 
40
62
  @test = Test.new
41
- @test.description = ""
63
+ @test.file_name = file_name
64
+ @test.description = ''
42
65
  @test.steps = []
43
66
  @test.errors = {}
44
67
  @test.tags = []
@@ -51,10 +74,11 @@ module RainforestCli::TestParser
51
74
  def process
52
75
  scratch = []
53
76
 
54
- text.lines.map(&:chomp).each_with_index do |line, line_no|
77
+ text.lines.each_with_index do |line, line_no|
78
+ line = line.chomp
55
79
  if line[0..1] == '#!'
56
80
  # special comment, don't ignore!
57
- @test.rfml_id = line[2..-1].strip.split(" ")[0]
81
+ @test.rfml_id = line[2..-1].strip.split(' ')[0]
58
82
  @test.description += line[1..-1] + "\n"
59
83
 
60
84
  elsif line[0] == '#'
@@ -65,7 +89,7 @@ module RainforestCli::TestParser
65
89
  next unless line[1..-1].strip[0..(field.length)] == "#{field}:"
66
90
 
67
91
  # extract just the text of the field
68
- @test[field] = line[1..-1].split(" ")[1..-1].join(" ").strip
92
+ @test[field] = line[1..-1].split(' ')[1..-1].join(' ').strip
69
93
 
70
94
  # if it's supposed to be a CSV field, split and trim it
71
95
  if CSV_FIELDS.include?(field)
@@ -82,9 +106,9 @@ module RainforestCli::TestParser
82
106
 
83
107
  elsif scratch.count == 1
84
108
  if line.strip == ''
85
- @test.errors[line_no] = Error.new(line_no, "Missing question")
109
+ @test.errors[line_no] = Error.new(line_no, 'Missing question')
86
110
  elsif !line.include?('?')
87
- @test.errors[line_no] = Error.new(line_no, "Missing ?")
111
+ @test.errors[line_no] = Error.new(line_no, 'Missing ?')
88
112
  else
89
113
  scratch << line.strip
90
114
  end
@@ -102,7 +126,7 @@ module RainforestCli::TestParser
102
126
  end
103
127
 
104
128
  if @test.rfml_id == nil
105
- @test.errors[0] = Error.new(0, "Missing RFML ID. Please start a line #! followed by a unique id.")
129
+ @test.errors[0] = Error.new(0, 'Missing RFML ID. Please start a line #! followed by a unique id.')
106
130
  end
107
131
 
108
132
  return @test
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+ require 'rainforest'
3
+ require 'parallel'
4
+ require 'ruby-progressbar'
5
+
6
+ class RainforestCli::Uploader
7
+ attr_reader :test_files, :remote_tests, :validator
8
+
9
+ def initialize(options)
10
+ @test_files = RainforestCli::TestFiles.new(options.test_folder)
11
+ @remote_tests = RainforestCli::RemoteTests.new(options.token)
12
+ @validator = RainforestCli::Validator.new(options, test_files, remote_tests)
13
+ end
14
+
15
+ def upload
16
+ validator.validate_with_exception!
17
+
18
+ # Create new tests first to ensure that they can be embedded
19
+ if new_tests.any?
20
+ logger.info 'Syncing new tests...'
21
+ each_in_parallel(new_tests) { |rfml_test| upload_empty_test(rfml_test) }
22
+ end
23
+
24
+ # Update all tests
25
+ logger.info 'Uploading tests...'
26
+ each_in_parallel(rfml_tests) { |rfml_test| upload_test(rfml_test) }
27
+ end
28
+
29
+ private
30
+
31
+ def each_in_parallel(tests, &blk)
32
+ progress_bar = ProgressBar.create(title: 'Rows', total: tests.count, format: '%a %B %p%% %t')
33
+ Parallel.each(tests, in_threads: threads, finish: lambda { |_item, _i, _result| progress_bar.increment }) do |rfml_test|
34
+ blk.call(rfml_test)
35
+ end
36
+ end
37
+
38
+ def rfml_tests
39
+ @rfml_tests ||= test_files.test_data
40
+ end
41
+
42
+ def new_tests
43
+ @new_tests ||= rfml_tests.select { |t| primary_key_dictionary[t.rfml_id].nil? }
44
+ end
45
+
46
+ def primary_key_dictionary
47
+ @primary_key_dictionary ||= remote_tests.primary_key_dictionary
48
+ end
49
+
50
+ def upload_empty_test(rfml_test)
51
+ test_obj = {
52
+ title: rfml_test.title,
53
+ start_uri: rfml_test.start_uri,
54
+ rfml_id: rfml_test.rfml_id
55
+ }
56
+ rf_test = Rainforest::Test.create(test_obj)
57
+
58
+ primary_key_dictionary[rfml_test.rfml_id] = rf_test.id
59
+ end
60
+
61
+ def upload_test(rfml_test)
62
+ return unless rfml_test.steps.count > 0
63
+
64
+ test_obj = create_test_obj(rfml_test)
65
+ begin
66
+ Rainforest::Test.update(primary_key_dictionary[rfml_test.rfml_id], test_obj)
67
+ rescue => e
68
+ logger.fatal "Error: #{rfml_test.rfml_id}: #{e}"
69
+ exit 2
70
+ end
71
+ end
72
+
73
+ def threads
74
+ RainforestCli::THREADS
75
+ end
76
+
77
+ def logger
78
+ RainforestCli.logger
79
+ end
80
+
81
+ def create_test_obj(rfml_test)
82
+ test_obj = {
83
+ start_uri: rfml_test.start_uri || '/',
84
+ title: rfml_test.title,
85
+ description: rfml_test.description,
86
+ source: 'rainforest-cli',
87
+ tags: rfml_test.tags.uniq,
88
+ rfml_id: rfml_test.rfml_id
89
+ }
90
+
91
+ test_obj[:elements] = rfml_test.steps.map do |step|
92
+ if step.respond_to?(:rfml_id)
93
+ step.to_element(primary_key_dictionary[step.rfml_id])
94
+ else
95
+ step.to_element
96
+ end
97
+ end
98
+
99
+ unless rfml_test.browsers.empty?
100
+ test_obj[:browsers] = rfml_test.browsers.map do|b|
101
+ {'state' => 'enabled', 'name' => b}
102
+ end
103
+ end
104
+
105
+ test_obj
106
+ end
107
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+ class RainforestCli::Validator
3
+ API_TOKEN_ERROR = 'Please supply API token and try again'
4
+ VALIDATIONS_PASSED = '[VALID]'
5
+ VALIDATIONS_FAILED = '[INVALID] - Please see log to correct errors.'
6
+
7
+ attr_reader :local_tests, :remote_tests
8
+
9
+ def initialize(options, local_tests = nil, remote_tests = nil)
10
+ @local_tests = local_tests || RainforestCli::TestFiles.new(options.test_folder)
11
+ @remote_tests = remote_tests || RainforestCli::RemoteTests.new(options.token)
12
+ end
13
+
14
+ def validate
15
+ check_test_directory_for_tests!
16
+ invalid?
17
+ end
18
+
19
+ def validate_with_exception!
20
+ check_test_directory_for_tests!
21
+
22
+ unless remote_tests.api_token_set?
23
+ logger.error API_TOKEN_ERROR
24
+ exit 2
25
+ end
26
+
27
+ exit 1 if invalid?
28
+ end
29
+
30
+ def invalid?
31
+ # Assign result to variables to ensure both methods are called
32
+ # (no short-circuiting with ||)
33
+ parsing_errors = has_parsing_errors?
34
+ dependency_errors = has_test_dependency_errors?
35
+ is_invalid = parsing_errors || dependency_errors
36
+
37
+ logger.info ''
38
+ logger.info(is_invalid ? VALIDATIONS_FAILED : VALIDATIONS_PASSED)
39
+ is_invalid
40
+ end
41
+
42
+ private
43
+
44
+ def check_test_directory_for_tests!
45
+ unless local_tests.count > 0
46
+ logger.error "No tests found in directory: #{local_tests.test_folder}"
47
+ exit 3
48
+ end
49
+ end
50
+
51
+ def has_parsing_errors?
52
+ logger.info 'Validating parsing errors...'
53
+ has_parsing_errors = rfml_tests.select { |t| t.errors.any? }
54
+
55
+ return false unless has_parsing_errors.any?
56
+
57
+ parsing_error_notification(has_parsing_errors)
58
+ true
59
+ end
60
+
61
+ def has_test_dependency_errors?
62
+ logger.info 'Validating embedded test IDs...'
63
+
64
+ # Assign result to variables to ensure both methods are called
65
+ nonexisting_tests = has_nonexisting_tests?
66
+ circular_dependencies = has_circular_dependencies?
67
+ nonexisting_tests || circular_dependencies
68
+ end
69
+
70
+ def has_nonexisting_tests?
71
+ contains_nonexistent_ids = rfml_tests.select { |t| (t.embedded_ids - all_rfml_ids).any? }
72
+
73
+ return false unless contains_nonexistent_ids.any?
74
+
75
+ nonexisting_embedded_id_notification(contains_nonexistent_ids)
76
+ true
77
+ end
78
+
79
+ def has_circular_dependencies?
80
+ # TODO: Check embedded tests for remote tests as well. The Rainforest Ruby client
81
+ # doesn't appear to support elements yet.
82
+ has_circular_dependencies = false
83
+ rfml_tests.each do |rfml_test|
84
+ has_circular_dependencies ||= check_for_nested_embed(rfml_test, rfml_test.rfml_id, rfml_test.file_name)
85
+ end
86
+ has_circular_dependencies
87
+ end
88
+
89
+ def check_for_nested_embed(rfml_test, root_id, root_file)
90
+ rfml_test.embedded_ids.each do |embed_id|
91
+ descendant = test_dictionary[embed_id]
92
+
93
+ # existence for embedded tests is covered in #has_nonexisting_tests?
94
+ next unless descendant
95
+
96
+ if descendant.embedded_ids.include?(root_id)
97
+ circular_dependencies_notification(root_file, descendant.file_name) if descendant.embedded_ids.include?(root_id)
98
+ return true
99
+ end
100
+
101
+ check_for_nested_embed(descendant, root_id, root_file)
102
+ end
103
+ false
104
+ end
105
+
106
+ def rfml_tests
107
+ @rfml_tests ||= local_tests.test_data
108
+ end
109
+
110
+ def all_rfml_ids
111
+ local_rfml_ids + remote_rfml_ids
112
+ end
113
+
114
+ def local_rfml_ids
115
+ @local_rfml_ids ||= local_tests.rfml_ids
116
+ end
117
+
118
+ def remote_rfml_ids
119
+ @remote_rfml_ids ||= remote_tests.rfml_ids
120
+ end
121
+
122
+ def test_dictionary
123
+ @test_dictionary ||= local_tests.test_dictionary
124
+ end
125
+
126
+ def parsing_error_notification(rfml_tests)
127
+ logger.error 'Parsing errors:'
128
+ logger.error ''
129
+ rfml_tests.each do |rfml_test|
130
+ logger.error "\t#{rfml_test.file_name}"
131
+ rfml_test.errors.each do |_line, error|
132
+ logger.error "\t#{error}"
133
+ end
134
+ end
135
+ logger.error ''
136
+ end
137
+
138
+ def nonexisting_embedded_id_notification(rfml_tests)
139
+ logger.error 'The following files contain unknown embedded test IDs:'
140
+ logger.error ''
141
+ rfml_tests.each do |rfml_test|
142
+ logger.error "\t#{rfml_test.file_name}"
143
+ end
144
+ logger.error ''
145
+ end
146
+
147
+ def circular_dependencies_notification(file_a, file_b)
148
+ logger.error 'The following files are embedding one another:'
149
+ logger.error ''
150
+ logger.error "\t#{file_a}"
151
+ logger.error "\t#{file_b}"
152
+ logger.error ''
153
+ end
154
+
155
+ def logger
156
+ RainforestCli.logger
157
+ end
158
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module RainforestCli
2
- VERSION = "1.2.0"
3
+ VERSION = '1.2.1'
3
4
  end
@@ -1,27 +1,29 @@
1
+ # frozen_string_literal: true
1
2
  # coding: utf-8
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'rainforest/cli/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "rainforest-cli"
8
+ spec.name = 'rainforest-cli'
8
9
  spec.version = RainforestCli::VERSION
9
- spec.authors = ["Simon Mathieu", "Russell Smith"]
10
- spec.email = ["simon@rainforestqa.com", "russ@rainforestqa.com"]
10
+ spec.authors = ['Russell Smith', 'Edward Paulet']
11
+ spec.email = ['russ@rainforestqa.com', 'edward@rainforestqa.com']
11
12
  spec.description = %q{Command line utility for Rainforest QA}
12
13
  spec.summary = %q{Command line utility for Rainforest QA}
13
- spec.homepage = "https://www.rainforestqa.com/"
14
- spec.license = "MIT"
14
+ spec.homepage = 'https://www.rainforestqa.com/'
15
+ spec.license = 'MIT'
15
16
 
16
17
  spec.files = `git ls-files`.split($/)
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
20
21
 
21
- spec.add_dependency "httparty"
22
- spec.add_dependency "parallel"
23
- spec.add_dependency "ruby-progressbar"
24
- spec.add_dependency "rainforest"
25
- spec.add_development_dependency "bundler", "~> 1.3"
26
- spec.add_development_dependency "rake"
22
+ spec.add_dependency 'httparty', '~> 0.13.7'
23
+ spec.add_dependency 'parallel', '~> 1.6', '>= 1.6.1'
24
+ spec.add_dependency 'ruby-progressbar', '~> 1.7', '>= 1.7.5'
25
+ spec.add_dependency 'rainforest', '~> 2.1', '>= 2.1.0'
26
+ spec.add_dependency 'http-exceptions', '~> 0.0', '>= 0.0.4'
27
+ spec.add_development_dependency 'bundler', '~> 1.3'
28
+ spec.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
27
29
  end
data/spec/cli_spec.rb CHANGED
@@ -1,45 +1,46 @@
1
+ # frozen_string_literal: true
1
2
  describe RainforestCli do
2
- let(:http_client) { RainforestCli::HttpClient.any_instance }
3
+ let(:http_client) { RainforestCli::HttpClient }
3
4
 
4
5
  before do
5
- Kernel.stub(:sleep)
6
+ allow(Kernel).to receive(:sleep)
6
7
  end
7
8
 
8
- describe ".start" do
9
+ describe '.start' do
9
10
  let(:valid_args) { %w(run --token foo run --fg all) }
10
11
  let(:ok_progress) do
11
12
  {
12
- "state" => "in_progress",
13
- "current_progress" => {"percent" => "1"},
14
- "state_details" => { "is_final_state" => false },
15
- "result" => "no_result",
13
+ 'state' => 'in_progress',
14
+ 'current_progress' => {'percent' => '1'},
15
+ 'state_details' => { 'is_final_state' => false },
16
+ 'result' => 'no_result',
16
17
  }
17
18
  end
18
19
 
19
20
  let(:complete_response) do
20
21
  {
21
- "state" => "complete",
22
- "current_progress" => {"percent" => "100"},
23
- "state_details" => { "is_final_state" => true},
24
- "result" => "passed",
22
+ 'state' => 'complete',
23
+ 'current_progress' => {'percent' => '100'},
24
+ 'state_details' => { 'is_final_state' => true},
25
+ 'result' => 'passed',
25
26
  }
26
27
  end
27
28
 
28
- context "with bad parameters" do
29
- context "no token" do
29
+ context 'with bad parameters' do
30
+ context 'no token' do
30
31
  let(:params) { %w(run --custom-url http://ad-hoc.example.com) }
31
32
  it 'errors out' do
32
33
  expect_any_instance_of(Logger).to receive(:fatal).with('You must pass your API token using: --token TOKEN')
33
- expect {
34
+ expect do
34
35
  described_class.start(params)
35
- }.to raise_error(SystemExit) { |error|
36
+ end.to raise_error(SystemExit) { |error|
36
37
  expect(error.status).to eq 2
37
38
  }
38
39
  end
39
40
  end
40
41
  end
41
42
 
42
- context "git-trigger" do
43
+ context 'git-trigger' do
43
44
  let(:params) { %w(run --token x --git-trigger) }
44
45
  let(:commit_message) { 'a test commit message' }
45
46
 
@@ -52,156 +53,149 @@ describe RainforestCli do
52
53
  end
53
54
 
54
55
  before do
55
- RainforestCli::GitTrigger.stub(:last_commit_message) { commit_message }
56
+ allow(RainforestCli::GitTrigger).to receive(:last_commit_message).and_return(commit_message)
56
57
  end
57
58
 
58
- describe "with tags parameter passed" do
59
+ describe 'with tags parameter passed' do
59
60
  let(:params) { %w(run --token x --tag x --git-trigger) }
60
61
 
61
- it "warns about the parameter being ignored" do
62
- expect_any_instance_of(Logger).to receive(:warn).with("Specified tags are ignored when using --git-trigger")
62
+ it 'warns about the parameter being ignored' do
63
+ expect_any_instance_of(Logger).to receive(:warn).with('Specified tags are ignored when using --git-trigger')
63
64
 
64
65
  start_with_params(params, 0)
65
66
  end
66
67
  end
67
68
 
68
- describe "without tags parameter passed" do
69
+ describe 'without tags parameter passed' do
69
70
  let(:params) { %w(run all --token x --git-trigger) }
70
71
 
71
- it "warns about the parameter being ignored" do
72
- expect_any_instance_of(Logger).to receive(:warn).with("Specified tests are ignored when using --git-trigger")
72
+ it 'warns about the parameter being ignored' do
73
+ expect_any_instance_of(Logger).to receive(:warn).with('Specified tests are ignored when using --git-trigger')
73
74
 
74
75
  start_with_params(params, 0)
75
76
  end
76
77
  end
77
78
 
78
- describe "with no @rainforest in the commit message" do
79
+ describe 'with no @rainforest in the commit message' do
79
80
  it "exit 0's and logs the reason" do
80
- expect_any_instance_of(Logger).to receive(:info).with("Not triggering as @rainforest was not mentioned in last commit message.")
81
+ expect_any_instance_of(Logger).to receive(:info)
82
+ .with('Not triggering as @rainforest was not mentioned in last commit message.')
81
83
  start_with_params(params, 0)
82
84
  end
83
85
  end
84
86
 
85
- describe "with @rainforest in the commit message, but no tags" do
87
+ describe 'with @rainforest in the commit message, but no tags' do
86
88
  let(:commit_message) { 'a test commit message @rainforest' }
87
89
 
88
90
  it "exit 2's and logs the reason" do
89
- expect_any_instance_of(Logger).to receive(:error).with("Triggered via git, but no hashtags detected. Please use commit message format:")
90
- expect_any_instance_of(Logger).to receive(:error).with("\t'some message. @rainforest #tag1 #tag2")
91
+ expect_any_instance_of(Logger).to receive(:error)
92
+ .with('Triggered via git, but no hashtags detected. Please use commit message format:')
93
+ expect_any_instance_of(Logger).to receive(:error)
94
+ .with("\t'some message. @rainforest #tag1 #tag2")
91
95
 
92
96
  start_with_params(params, 2)
93
97
  end
94
98
  end
95
99
 
96
- describe "with @rainforest in the commit message + hashtags" do
100
+ describe 'with @rainforest in the commit message + hashtags' do
97
101
  let(:commit_message) { 'a test commit message @rainforest #run-me' }
98
102
 
99
- it "starts the run with the specified tags" do
100
- http_client.should_receive(:post) do |url, options|
101
- expect(url).to eq("/runs")
102
- expect(options[:tags]).to eq(['run-me'])
103
- {}
104
- end
103
+ it 'starts the run with the specified tags' do
104
+ expect_any_instance_of(http_client).to receive(:post)
105
+ .with('/runs', tags: ['run-me']).and_return({})
105
106
 
106
107
  start_with_params(params, 0)
107
108
  end
108
109
  end
109
110
  end
110
111
 
111
- context "with site-id and custom-url" do
112
+ context 'with site-id and custom-url' do
112
113
  let(:params) { %w(run --token x --site 3 --custom-url http://ad-hoc.example.com) }
113
- it "creates a new environment" do
114
- http_client.should_receive(:post).with("/environments",
115
- {
116
- :name => "temporary-env-for-custom-url-via-CLI",
117
- :url=>"http://ad-hoc.example.com"
118
- }
119
- ).and_return(
120
- { 'id' => 333 }
121
- )
122
-
123
- http_client.should_receive(:post).with("/runs", anything).and_return( { "id" => 1 } )
114
+ it 'creates a new environment' do
115
+ expect_any_instance_of(http_client).to receive(:post)
116
+ .with('/environments', name: 'temporary-env-for-custom-url-via-CLI', url: 'http://ad-hoc.example.com')
117
+ .and_return({ 'id' => 333 })
118
+
119
+ expect_any_instance_of(http_client).to receive(:post).with('/runs', anything)
120
+ .and_return({ 'id' => 1 })
124
121
 
125
122
  # This is a hack because when expecting a function to be called with
126
123
  # parameters, the last call is compared but I want to compare the first
127
124
  # call, not the call to create a run, so I exit, but rescue from it here
128
125
  # so that the spec doesn't fail. It's horrible, sorry!
126
+
127
+ # NOTE: start Rubocop exception
128
+ # rubocop:disable Lint/HandleExceptions
129
129
  begin
130
130
  described_class.start(params)
131
- rescue SystemExit => e
131
+ rescue SystemExit
132
132
  # That's fine, this is expected but tested in a differnet assertion
133
133
  end
134
+ # rubocop:enable Lint/HandleExceptions
135
+ # NOTE: end Rubocop exception
134
136
  end
135
137
 
136
- it "starts the run with site_id and environment_id" do
137
- RainforestCli::Runner.any_instance.stub(get_environment_id: 333)
138
+ it 'starts the run with site_id and environment_id' do
139
+ allow_any_instance_of(RainforestCli::Runner).to receive(:get_environment_id).and_return(333)
138
140
 
139
- http_client.should_receive(:post).with(
140
- "/runs",
141
- { :tests=>[], :site_id=>3, :environment_id=>333 }
142
- ).and_return( {} )
141
+ expect_any_instance_of(http_client).to receive(:post).with(
142
+ '/runs',
143
+ { tests: [], site_id: 3, environment_id: 333 }
144
+ ).and_return({})
143
145
  described_class.start(params)
144
146
  end
145
147
  end
146
148
 
147
- context "with environment-id" do
149
+ context 'with environment-id' do
148
150
  let(:params) { %w(run --token x --environment 123) }
149
151
 
150
- it "starts the run with environment_id" do
151
- RainforestCli::Runner.any_instance.stub(get_environment_id: 333)
152
+ it 'starts the run with environment_id' do
153
+ allow_any_instance_of(RainforestCli::Runner).to receive(:get_environment_id)
154
+ .and_return(333)
152
155
 
153
- http_client.should_receive(:post).with(
154
- "/runs",
155
- { :tests=>[], :environment_id=>123 }
156
- ).and_return( {} )
156
+ expect_any_instance_of(http_client).to receive(:post).with(
157
+ '/runs',
158
+ { tests: [], environment_id: 123 }
159
+ ).and_return({})
157
160
  described_class.start(params)
158
161
  end
159
162
  end
160
163
 
161
- context "with smart_folder_id" do
164
+ context 'with smart_folder_id' do
162
165
  let(:params) { %w(run --token x --folder 123) }
163
166
 
164
- it "starts the run with smart folder" do
165
- http_client.should_receive(:post).with(
166
- "/runs",
167
- { :smart_folder_id=>123 }
168
- ).and_return( {} )
167
+ it 'starts the run with smart folder' do
168
+ expect_any_instance_of(http_client).to receive(:post).with(
169
+ '/runs',
170
+ { smart_folder_id: 123 }
171
+ ).and_return({})
169
172
  described_class.start(params)
170
173
  end
171
174
  end
172
175
 
173
- context "a simple run" do
176
+ context 'a simple run' do
174
177
  before do
175
- http_client.stub(:post) { {"id" => 1} }
176
- 3.times do
177
- http_client.should_receive(:get) { ok_progress }
178
- end
179
- http_client.should_receive(:get) { complete_response }
178
+ allow_any_instance_of(http_client).to receive(:post).and_return('id' => 1)
179
+ expect_any_instance_of(http_client).to receive(:get).exactly(3).times.and_return(ok_progress)
180
+ expect_any_instance_of(http_client).to receive(:get).and_return(complete_response)
180
181
  end
181
182
 
182
- it "should return true" do
183
- described_class.start(valid_args).should be_true
183
+ it 'should return true' do
184
+ expect(described_class.start(valid_args)).to eq(true)
184
185
  end
185
186
  end
186
187
 
187
- context "a run where the server 500s after a while" do
188
+ context 'a run where the server 500s after a while' do
188
189
  before do
189
- http_client.stub(:post) { {"id" => 1} }
190
- 2.times do
191
- http_client.should_receive(:get) { ok_progress }
192
- end
193
-
194
- http_client.should_receive(:get) { nil }
195
-
196
- 2.times do
197
- http_client.should_receive(:get) { ok_progress }
198
- end
199
-
200
- http_client.should_receive(:get) { complete_response }
190
+ allow_any_instance_of(http_client).to receive(:post).and_return('id' => 1)
191
+ expect_any_instance_of(http_client).to receive(:get).twice.and_return(ok_progress)
192
+ expect_any_instance_of(http_client).to receive(:get)
193
+ expect_any_instance_of(http_client).to receive(:get).twice.and_return(ok_progress)
194
+ expect_any_instance_of(http_client).to receive(:get).and_return(complete_response)
201
195
  end
202
196
 
203
- it "should return true" do
204
- described_class.start(valid_args).should be_true
197
+ it 'should return true' do
198
+ expect(described_class.start(valid_args)).to eq(true)
205
199
  end
206
200
  end
207
201
  end