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