rainforest-cli 1.2.0 → 1.2.1

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 (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