itriagetestrail 1.0.36

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7d3fb08d0333d280c30606a5cd177b439f668a94a8683bf0099c47dac4b563cc
4
+ data.tar.gz: 9dd89450a238a488c46783754df2ca4c312301e2e9dd807b31d30f01b80430e7
5
+ SHA512:
6
+ metadata.gz: 64729b306598a415b93d91869a57d0be086772f39fe235159be11b17ea3e88c48186189207fe125ab0d93d9d9640db9de0da1817a829e210de94c89c4e41ce6d
7
+ data.tar.gz: ed2d5d73ea6c95e07d56f0b8355e0ec73b9d09420c083517e92f7500efa10f9971bc1822c96daf8d8b94834f272b393d33c986659251151bcfeacfe2d99e2db9
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'itriagetestrail/version'
4
+ require_relative 'itriagetestrail/testrail_binding'
5
+
6
+ require_relative 'itriagetestrail/pool'
7
+ require_relative 'itriagetestrail/testrail_objects/milestones'
8
+ require_relative 'itriagetestrail/testrail_objects/projects'
9
+ require_relative 'itriagetestrail/testrail_objects/sections'
10
+ require_relative 'itriagetestrail/testrail_objects/suites'
11
+ require_relative 'itriagetestrail/testrail_objects/test_cases'
12
+ require_relative 'itriagetestrail/testrail_objects/test_plans'
13
+ require_relative 'itriagetestrail/testrail_objects/test_results'
14
+ require_relative 'itriagetestrail/testrail_objects/test_runs'
15
+
16
+ require 'tzinfo'
17
+
18
+ module Itriagetestrail
19
+ class TestRailInterface
20
+ include Projects
21
+ include Suites
22
+ include Milestones
23
+ include Sections
24
+ include TestCases
25
+ include TestPlans
26
+ include TestRuns
27
+ include TestResults
28
+
29
+ attr_accessor :client
30
+ attr_accessor :sections
31
+ attr_accessor :test_cases
32
+ attr_accessor :test_case_ids
33
+ attr_accessor :run_id
34
+ attr_accessor :execute
35
+ attr_accessor :pool
36
+ attr_accessor :batch_size
37
+ attr_reader :results
38
+ attr_reader :external_results
39
+
40
+ def _time_zone
41
+ @time_zone = if @testrail_config[:tzinfoTimeZone].nil? || @testrail_config[:tzinfoTimeZone].empty?
42
+ TZInfo::Timezone.get('UTC')
43
+ else
44
+ TZInfo::Timezone.get(@testrail_config[:tzinfoTimeZone])
45
+ end
46
+ end
47
+
48
+ def testrail_online
49
+ # This is the very first call to TestRail
50
+ make_connection
51
+ projects
52
+ if @client.response_code == '200' || @client.response_code.nil?
53
+ true
54
+ else
55
+ puts "**** TESTRAIL IS OFFLINE for maintenance or other reason with status code #{@client.response_code}"
56
+ false
57
+ end
58
+ end
59
+
60
+ def _execute
61
+ @execute = if @testrail_config[:user].nil? || @testrail_config[:user].empty?
62
+ false
63
+ else
64
+ testrail_online
65
+ end
66
+ end
67
+
68
+ def initialize
69
+ @setup = false
70
+ config
71
+
72
+ if File.exist?(run_tempfile_name)
73
+ @run_id = File.read(run_tempfile_name)
74
+ @plan_id = File.read(plan_tempfile_name)
75
+ @shared_run = true
76
+ else
77
+ @run_id = 0
78
+ @plan_id = 0
79
+ end
80
+
81
+ @milestone_name = normalize_milestone
82
+ @jenkins_build = @testrail_config[:jenkinsBuild]
83
+ @app_version = @testrail_config[:appVersion]
84
+ @suite_name = @testrail_config[:suiteName]
85
+
86
+ _time_zone
87
+
88
+ _execute
89
+
90
+ clear_results
91
+ @submitted = { results: [] }
92
+
93
+ @external_results = { results: [] }
94
+ @batch_size = @testrail_config[:batch_size] || 0
95
+ end
96
+
97
+ def clear_results
98
+ @results = { results: [] }
99
+ end
100
+
101
+ def config
102
+ time_zone = if ENV['TZINFO_TIME_ZONE'].nil? || ENV['TZINFO_TIME_ZONE'].empty?
103
+ 'America/Denver'
104
+ else
105
+ ENV['TZINFO_TIME_ZONE']
106
+ end
107
+
108
+ @testrail_config = {
109
+ site: ENV['TESTRAIL_SITE'],
110
+ user: ENV['TESTRAIL_USER'],
111
+ password: ENV['TESTRAIL_PASSWORD'],
112
+ tzinfoTimeZone: time_zone,
113
+ projectId: ENV['TESTRAIL_PROJECT_ID'],
114
+ milestone: ENV['TESTRAIL_MILESTONE'],
115
+ projectName: ENV['APP_NAME'],
116
+ suiteName: ENV['TESTRAIL_SUITE_NAME'],
117
+ run_id: ENV['TESTRAIL_RUN_ID'],
118
+ report_skips: ENV['TESTRAIL_REPORT_SKIPS'],
119
+ close_run: ENV['TESTRAIL_CLOSE_RUN'],
120
+ close_run_delay: ENV['TESTRAIL_CLOSE_RUN_DELAY'],
121
+ origin: ENV['GIT_BRANCH'],
122
+ jenkinsBuild: ENV['BUILD_NUMBER'],
123
+ appVersion: nil,
124
+ config: { ios: ENV['IOS_VERSION'],
125
+ android: ENV['ANDROID_VERSION'],
126
+ androidDevice: ENV['DEVICE_NAME'],
127
+ browser: ENV['BROWSER_NAME'], # Jenkins: SELENIUM_BROWSER
128
+ platform: ENV['PLATFORM'], # Jenkins: SELENIUM_PLATFORM
129
+ browserVersion: ENV['BROWSER_VERSION'] } # Jenkins: SELENIUM_VERSION
130
+ }
131
+ end
132
+
133
+ # These are generic, hardcoded tag translations that map to test ids in TestRail
134
+ def tag_ids(tag_names = [])
135
+ tag_ids = []
136
+ tag_names.each do |tag_name|
137
+ case tag_name
138
+ when '@wip'
139
+ tag_ids << 1
140
+ when '@flaky'
141
+ tag_ids << 2
142
+ when '@broken'
143
+ tag_ids << 3
144
+ when '@missingTheSauce'
145
+ tag_ids << 4
146
+ end
147
+ end
148
+ tag_ids
149
+ end
150
+
151
+ def setup?
152
+ @setup
153
+ end
154
+
155
+ def setup
156
+ return false if setup?
157
+ return false unless execute
158
+
159
+ @results = { results: [] }
160
+ @submitted = { results: [] }
161
+
162
+ @external_results = { results: [] }
163
+ @batch_size = @testrail_config[:batch_size] || 0
164
+
165
+ initialize_variables
166
+
167
+ @milestone_id = fetch_milestone(@milestone_name)
168
+
169
+ reset_milestone(@milestone_name)
170
+
171
+ #Supports tagging
172
+ @testrail_case_fields = @client.send_get('get_case_fields')
173
+ @pool = Pool.new(1)
174
+ @setup = true
175
+ end
176
+
177
+ def create_run_id
178
+ # TODO: Look into configuration_ids to add a plan
179
+ add_testrail_run if @run_id.to_i.zero?
180
+ end
181
+
182
+ def initialize_variables
183
+ make_connection
184
+ # Set the project id
185
+ set_project
186
+ # Get the test rail suites
187
+ testrail_suites
188
+ # Get the test rail sections
189
+ testrail_sections
190
+ # Get the test rail ids
191
+ testrail_ids
192
+ # Populate configurations
193
+ configurations
194
+ end
195
+
196
+ def run_tempfile_name
197
+ "./tmp/#{@testrail_config[:jenkinsBuild]}_testrail_run_id"
198
+ end
199
+
200
+ def plan_tempfile_name
201
+ "./tmp/#{@testrail_config[:jenkinsBuild]}_testrail_plan_id"
202
+ end
203
+
204
+ def make_connection
205
+ @client = TestRail::APIClient.new(@testrail_config[:site])
206
+ @client.user = @testrail_config[:user]
207
+ @client.password = @testrail_config[:password]
208
+ end
209
+
210
+ def close_run?
211
+ # do not close a run if a run id was supplied. other test suites related to the job will need it.
212
+ close_run = false
213
+ if @testrail_config[:close_run] == 'true' && (@testrail_config[:run_id].nil? || @testrail_config[:run_id].empty?)
214
+ close_run = true
215
+ end
216
+ close_run
217
+ end
218
+
219
+ # This method is only used publicly
220
+ def delete_temp_files
221
+ File.delete(run_tempfile_name) if File.exist?(run_tempfile_name)
222
+ File.delete(plan_tempfile_name) if File.exist?(plan_tempfile_name)
223
+ end
224
+
225
+ # This method is only used publicly
226
+ def initialize_temp_files
227
+ Dir.mkdir('./tmp') unless File.exist?('./tmp')
228
+ File.write(run_tempfile_name, @run_id)
229
+ File.write(plan_tempfile_name, @plan_id)
230
+
231
+ @shared_run = true
232
+ end
233
+
234
+ def close_run(run_id = @run_id, message = '')
235
+ # A delay is recommended to prevent an HTTP 400 race condition where final results are still
236
+ # being populated in the TESTRAIL DB while a close_run has been requested.
237
+
238
+ close_run_delay = @testrail_config[:close_run_delay] || 5
239
+ sleep close_run_delay.to_i
240
+ @client.send_post("update_run/#{run_id}",
241
+ include_all: false, case_ids: existing_cases_from_run(run_id),
242
+ description: 'Timestamp: ' + @time_zone.now.strftime('%m/%d/%Y %I:%M %p') +
243
+ "\nBranch: #{@testrail_config[:origin]}" + "\n#{message}")
244
+
245
+ @client.send_post("close_run/#{run_id}", {})
246
+ end
247
+
248
+ def shutdown(message = '')
249
+ @pool.shutdown
250
+
251
+ close_run(message) if close_run?
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'itriagetestrail'
4
+ module Itriagetestrail
5
+ class TRCucumber13 < TestRailInterface
6
+ def scenario_title_and_external_id(scenario)
7
+ if scenario.class.to_s == 'Cucumber::Ast::OutlineTable::ExampleRow'
8
+ scenario_cell_id = []
9
+ scenario.name.split('|')[1..50].each { |cell| scenario_cell_id << cell.gsub(/[\| "]/, '') }
10
+ @scenario_title = "#{scenario.scenario_outline.title}, Example: #{scenario_cell_id}"
11
+ @external_id = "#{scenario.scenario_outline.feature.name};#{@scenario_title}"[0, 249]
12
+ @scenario_steps = scenario.scenario_outline.raw_steps.select { 'name' }.collect(&:name).join("\n")
13
+ else
14
+ # identifiers: scenario.feature.name, scenario.name
15
+ @external_id = "#{scenario.feature.title};#{scenario.title}"
16
+ @scenario_title = scenario.title
17
+ @scenario_steps = scenario.steps.select { 'name' }.collect(&:name).join("\n")
18
+ end
19
+ @scenario_tags = scenario.source_tag_names
20
+ end
21
+
22
+ def record_result(scenario)
23
+ return if execute == false
24
+ begin
25
+ scenario_title_and_external_id(scenario)
26
+
27
+ if scenario.passed?
28
+ store_result(@scenario_title, @external_id, @scenario_steps, 1, '')
29
+ else
30
+ store_result(@scenario_title, @external_id, @scenario_steps,
31
+ (ENV['RERUN'] ? 5 : 4).to_s.to_i, "#{scenario.exception.class}\n" \
32
+ "#{scenario.exception}\n#{scenario.exception.backtrace}")
33
+ end
34
+ rescue StandardError => exception
35
+ puts "TESTRAIL ENCOUNTERED AN ERROR: #{exception}\n #{@external_id} \n\n"
36
+ end
37
+ end
38
+
39
+ def shutdown
40
+ return unless execute
41
+ sleep 3 # testrail service rate limit precaution
42
+ send_results_to_testrail
43
+ super
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'itriagetestrail'
4
+ module Itriagetestrail
5
+ class TRCucumber20 < TestRailInterface
6
+ def scenario_title_and_external_id(scenario)
7
+ if scenario.class.to_s == 'Cucumber::RunningTestCase::ScenarioOutlineExample'
8
+ scenario.cell_values.each { |cell| cell.gsub!(/\s+/, '') }
9
+ @scenario_title = "#{scenario.name.match(/\b.*Example/)}: #{scenario.cell_values}"
10
+ @external_id = "#{scenario.feature.short_name}\;#{@scenario_title}"[0, 249]
11
+ else
12
+ @external_id = "#{scenario.feature.short_name}\;#{scenario.name}"
13
+ @scenario_title = scenario.name
14
+ end
15
+ @scenario_tags = scenario.source_tag_names
16
+ end
17
+
18
+ def record_result(scenario)
19
+ return if execute == false
20
+ begin
21
+ scenario_title_and_external_id(scenario)
22
+
23
+ @scenario_steps = scenario.test_steps.select { 'name' }.collect(&:name).join("\n")
24
+
25
+ if scenario.passed?
26
+ store_result(@scenario_title, @external_id, @scenario_steps, 1, '')
27
+ else
28
+ store_result(@scenario_title, @external_id, @scenario_steps,
29
+ (ENV['RERUN'] ? 5 : 4).to_s.to_i, "#{scenario.exception.class}\n" \
30
+ "#{scenario.exception}\n#{scenario.exception.backtrace}")
31
+ end
32
+ rescue StandardError => exception
33
+ puts "TESTRAIL ENCOUNTERED AN ERROR: #{exception}\n #{@external_id} \n\n"
34
+ end
35
+ end
36
+
37
+ def shutdown
38
+ return unless execute
39
+ sleep 3 # testrail service rate limit precaution
40
+ send_results_to_testrail
41
+ super
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'itriagetestrail'
4
+ module Itriagetestrail
5
+ class TRCucumber30 < TestRailInterface
6
+ def scenario_title_and_external_id(scenario)
7
+ if scenario.class.to_s == 'Cucumber::RunningTestCase::ScenarioOutlineExample'
8
+ # Remove spaces within cells to match our Cucumber 1.3 implementation
9
+ scenario.cell_values.each { |cell| cell.gsub!(/\s+/, '') }
10
+ @scenario_title = "#{scenario.name.match(/\b.*Example/)}: #{scenario.cell_values}"
11
+ @external_id = "#{scenario.feature.short_name}\;#{@scenario_title}"[0, 249]
12
+ else
13
+ @external_id = "#{scenario.feature.short_name}\;#{scenario.name}"
14
+ @scenario_title = scenario.name
15
+ end
16
+ @scenario_tags = scenario.source_tag_names
17
+ end
18
+
19
+ def record_result(scenario)
20
+ return if execute == false
21
+ begin
22
+ scenario_title_and_external_id(scenario)
23
+ @scenario_steps = scenario.test_steps.collect(&:text).join("\n")
24
+
25
+ if scenario.passed?
26
+ store_result(@scenario_title, @external_id, @scenario_steps, 1, '')
27
+ else
28
+ store_result(@scenario_title, @external_id, @scenario_steps,
29
+ (ENV['RERUN'] ? 5 : 4).to_s.to_i, "#{scenario.exception.class}" \
30
+ "\n#{scenario.exception}\n#{scenario.exception.backtrace}")
31
+ end
32
+ rescue StandardError => exception
33
+ puts "TESTRAIL ENCOUNTERED AN ERROR: #{exception}\n #{@external_id} \n\n"
34
+ end
35
+ end
36
+
37
+ def shutdown
38
+ return unless execute
39
+ sleep 3 # testrail service rate limit precaution
40
+ send_results_to_testrail
41
+ super
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'itriagetestrail'
4
+ module Itriagetestrail
5
+ class TRMiniTest < TestRailInterface
6
+ def record_result(test, failures)
7
+ return if execute == false
8
+ begin
9
+ @external_id = "#{test.class}##{test.name}"
10
+ if test.passed?
11
+ store_result(test.name, @external_id, '', 1, '')
12
+ elsif failures.inspect.include? 'Minitest::Skip'
13
+ store_result(test.name, @external_id, '', 6, failures.inspect)
14
+ else
15
+ store_result(test.name, @external_id, '', 5, failures.inspect)
16
+ end
17
+ rescue StandardError => exception
18
+ puts "TESTRAIL ENCOUNTERED AN ERROR: #{exception}\n #{@external_id} \n\n"
19
+ end
20
+ end
21
+
22
+ def shutdown
23
+ return unless execute
24
+ sleep 3 # testrail service rate limit precaution
25
+ send_results_to_testrail
26
+ super
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pool
4
+ def initialize(size)
5
+ @size = size
6
+ @jobs = Queue.new
7
+
8
+ @pool = Array.new(@size) do |i|
9
+ Thread.new do
10
+ Thread.current[:id] = i
11
+
12
+ catch(:exit) do
13
+ loop do
14
+ job, args = @jobs.pop
15
+ job.call(*args)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def schedule(*args, &block)
23
+ @jobs << [block, args]
24
+ end
25
+
26
+ def shutdown
27
+ @size.times do
28
+ schedule { throw :exit }
29
+ end
30
+
31
+ @pool.map(&:join)
32
+ end
33
+ end