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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +7 -1
- data/README.md +2 -0
- data/Rakefile +6 -4
- data/circle.yml +3 -0
- data/lib/rainforest/cli.rb +20 -16
- data/lib/rainforest/cli/constants.rb +4 -0
- data/lib/rainforest/cli/csv_importer.rb +6 -6
- data/lib/rainforest/cli/git_trigger.rb +2 -1
- data/lib/rainforest/cli/http_client.rb +50 -13
- data/lib/rainforest/cli/options.rb +71 -39
- data/lib/rainforest/cli/remote_tests.rb +49 -0
- data/lib/rainforest/cli/runner.rb +19 -17
- data/lib/rainforest/cli/test_files.rb +32 -14
- data/lib/rainforest/cli/test_importer.rb +35 -155
- data/lib/rainforest/cli/test_parser.rb +38 -14
- data/lib/rainforest/cli/uploader.rb +107 -0
- data/lib/rainforest/cli/validator.rb +158 -0
- data/lib/rainforest/cli/version.rb +2 -1
- data/rainforest-cli.gemspec +14 -12
- data/spec/cli_spec.rb +84 -90
- data/spec/csv_importer_spec.rb +13 -8
- data/spec/git_trigger_spec.rb +28 -15
- data/spec/http_client_spec.rb +57 -0
- data/spec/options_spec.rb +72 -70
- data/spec/rainforest-example/example_test.rfml +2 -1
- data/spec/remote_tests_spec.rb +22 -0
- data/spec/runner_spec.rb +17 -16
- data/spec/spec_helper.rb +16 -9
- data/spec/test_files_spec.rb +20 -24
- data/spec/uploader_spec.rb +54 -0
- data/spec/validation-examples/circular_embeds/test1.rfml +5 -0
- data/spec/validation-examples/circular_embeds/test2.rfml +5 -0
- data/spec/validation-examples/correct_embeds/embedded_test.rfml +6 -0
- data/spec/validation-examples/correct_embeds/test_with_embedded.rfml +8 -0
- data/spec/validation-examples/missing_embeds/correct_test.rfml +8 -0
- data/spec/validation-examples/missing_embeds/incorrect_test.rfml +8 -0
- data/spec/validation-examples/parse_errors/no_parse_errors.rfml +6 -0
- data/spec/validation-examples/parse_errors/no_question.rfml +5 -0
- data/spec/validation-examples/parse_errors/no_question_mark.rfml +6 -0
- data/spec/validation-examples/parse_errors/no_rfml_id.rfml +5 -0
- data/spec/validator_spec.rb +119 -0
- 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
|
-
|
23
|
-
|
24
|
-
|
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(
|
38
|
-
@text =
|
59
|
+
def initialize(file_name)
|
60
|
+
@text = File.read(file_name).to_s
|
39
61
|
|
40
62
|
@test = Test.new
|
41
|
-
@test.
|
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.
|
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(
|
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(
|
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,
|
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,
|
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,
|
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
|
data/rainforest-cli.gemspec
CHANGED
@@ -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 =
|
8
|
+
spec.name = 'rainforest-cli'
|
8
9
|
spec.version = RainforestCli::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
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 =
|
14
|
-
spec.license =
|
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 = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
|
21
|
-
spec.add_dependency
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
24
|
-
spec.add_dependency
|
25
|
-
spec.
|
26
|
-
spec.add_development_dependency
|
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
|
3
|
+
let(:http_client) { RainforestCli::HttpClient }
|
3
4
|
|
4
5
|
before do
|
5
|
-
Kernel.
|
6
|
+
allow(Kernel).to receive(:sleep)
|
6
7
|
end
|
7
8
|
|
8
|
-
describe
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
29
|
-
context
|
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
|
-
|
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
|
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.
|
56
|
+
allow(RainforestCli::GitTrigger).to receive(:last_commit_message).and_return(commit_message)
|
56
57
|
end
|
57
58
|
|
58
|
-
describe
|
59
|
+
describe 'with tags parameter passed' do
|
59
60
|
let(:params) { %w(run --token x --tag x --git-trigger) }
|
60
61
|
|
61
|
-
it
|
62
|
-
expect_any_instance_of(Logger).to receive(:warn).with(
|
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
|
69
|
+
describe 'without tags parameter passed' do
|
69
70
|
let(:params) { %w(run all --token x --git-trigger) }
|
70
71
|
|
71
|
-
it
|
72
|
-
expect_any_instance_of(Logger).to receive(:warn).with(
|
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
|
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)
|
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
|
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)
|
90
|
-
|
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
|
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
|
100
|
-
http_client.
|
101
|
-
|
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
|
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
|
114
|
-
http_client.
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
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
|
137
|
-
RainforestCli::Runner.
|
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.
|
140
|
-
|
141
|
-
{ :
|
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
|
149
|
+
context 'with environment-id' do
|
148
150
|
let(:params) { %w(run --token x --environment 123) }
|
149
151
|
|
150
|
-
it
|
151
|
-
RainforestCli::Runner.
|
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.
|
154
|
-
|
155
|
-
{ :
|
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
|
164
|
+
context 'with smart_folder_id' do
|
162
165
|
let(:params) { %w(run --token x --folder 123) }
|
163
166
|
|
164
|
-
it
|
165
|
-
http_client.
|
166
|
-
|
167
|
-
{ :
|
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
|
176
|
+
context 'a simple run' do
|
174
177
|
before do
|
175
|
-
http_client.
|
176
|
-
3.times
|
177
|
-
|
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
|
183
|
-
described_class.start(valid_args).
|
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
|
188
|
+
context 'a run where the server 500s after a while' do
|
188
189
|
before do
|
189
|
-
http_client.
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
204
|
-
described_class.start(valid_args).
|
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
|