telly 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/telly +57 -0
- data/lib/telly.rb +289 -0
- data/lib/testrail.rb +106 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: daa063e14ee499acd934b81ead0304419094190b
|
4
|
+
data.tar.gz: a8f8240fe98212a75bca1f81c722981b735930cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9060bdcd028c788d6c2224b591d342d905510e2f9f75fab049ef4d8f92a241139d7d27888ae07d64141bfdc6958e54c81ddea93e6e907b205e6eb09a3a1ec4ec
|
7
|
+
data.tar.gz: 0f8d56d22cbccfe8d6e88650f2ab480db43bf88e561d003d40213f0aa224ad9efda3291e52d90f8cfbd8ce29b1bcd063acc2e6fd4b9adf33cb45f18cb4e070f4
|
data/bin/telly
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# == telly.rb
|
4
|
+
# This script adds test results in testrail from a finished beaker run
|
5
|
+
# It takes in a beaker junit file and a TestRail Testrun ID
|
6
|
+
#
|
7
|
+
# It matches the beaker tests with TestRail testcases by looking for the
|
8
|
+
# test case ID in the beaker script. The combination of a test run and a test case
|
9
|
+
# allows the script to add a result for a particular instance of a test case.
|
10
|
+
# In TestRail parlance, this is confusingly called a test.
|
11
|
+
# From the TestRail API docs:
|
12
|
+
# "In TestRail, tests are part of a test run and the test cases are part of the
|
13
|
+
# related test suite. So, when you create a new test run, TestRail creates a test
|
14
|
+
# for each test case found in the test suite of the run. You can therefore think
|
15
|
+
# of a test as an “instance” of a test case which can have test results, comments
|
16
|
+
# and a test status.""
|
17
|
+
|
18
|
+
require 'telly'
|
19
|
+
require 'optparse'
|
20
|
+
|
21
|
+
# Parses the command line options
|
22
|
+
# @return [Void]
|
23
|
+
def parse_opts
|
24
|
+
options_hash = {}
|
25
|
+
|
26
|
+
optparse = OptionParser.new do|parser|
|
27
|
+
options_hash = {
|
28
|
+
testrun_id: nil,
|
29
|
+
junit_file: nil,
|
30
|
+
}
|
31
|
+
|
32
|
+
parser.on( '-t', '--testrun-id TESTRUN_ID', 'The testrun id' ) do |testrun_id|
|
33
|
+
options_hash[:testrun_id] = testrun_id
|
34
|
+
end
|
35
|
+
|
36
|
+
parser.on( '-j', '--junit-folder JUNIT_FILE', 'Beaker junit file' ) do |junit_file|
|
37
|
+
options_hash[:junit_file] = junit_file
|
38
|
+
end
|
39
|
+
|
40
|
+
parser.on( '-h', '--help', 'Display this screen' ) do
|
41
|
+
puts parser
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
parser.parse!
|
46
|
+
|
47
|
+
if not options_hash[:testrun_id] or not options_hash[:junit_file]
|
48
|
+
puts "Error: Missing option(s)"
|
49
|
+
puts parser
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
options_hash
|
55
|
+
end
|
56
|
+
|
57
|
+
Telly::main(parse_opts)
|
data/lib/telly.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'yaml'
|
5
|
+
require 'pp'
|
6
|
+
require 'testrail'
|
7
|
+
|
8
|
+
# == telly.rb
|
9
|
+
# This module provides functions to add test results in testrail from a
|
10
|
+
# finished beaker run.
|
11
|
+
#
|
12
|
+
# It takes in a beaker junit file and a TestRail Testrun ID
|
13
|
+
#
|
14
|
+
# It matches the beaker tests with TestRail testcases by looking for the
|
15
|
+
# test case ID in the beaker script. The combination of a test run and a test case
|
16
|
+
# allows the script to add a result for a particular instance of a test case.
|
17
|
+
# In TestRail parlance, this is confusingly called a test.
|
18
|
+
# From the TestRail API docs:
|
19
|
+
# "In TestRail, tests are part of a test run and the test cases are part of the
|
20
|
+
# related test suite. So, when you create a new test run, TestRail creates a test
|
21
|
+
# for each test case found in the test suite of the run. You can therefore think
|
22
|
+
# of a test as an “instance” of a test case which can have test results, comments
|
23
|
+
# and a test status.""
|
24
|
+
module Telly
|
25
|
+
|
26
|
+
TESTRAIL_URL = 'https://testrail.ops.puppetlabs.net/'
|
27
|
+
CREDENTIALS_FILE = '~/.testrail_credentials.yaml'
|
28
|
+
|
29
|
+
# Used for extracted the test case ID from beaker scripts
|
30
|
+
TESTCASE_ID_REGEX = /.*(?<jira_ticket>\w+-\d+).*[cC](?<testrun_id>\d+)/
|
31
|
+
|
32
|
+
# Testrail Status IDs
|
33
|
+
PASSED = 1
|
34
|
+
BLOCKED = 2
|
35
|
+
FAILED = 5
|
36
|
+
|
37
|
+
def do_stub_test(credentials)
|
38
|
+
api = get_testrail_api(credentials)
|
39
|
+
|
40
|
+
api.send_post
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
##################################
|
45
|
+
# Main
|
46
|
+
##################################
|
47
|
+
|
48
|
+
# Run the importer
|
49
|
+
#
|
50
|
+
# @param [Hash] An optparse object
|
51
|
+
#
|
52
|
+
# @return [Void]
|
53
|
+
#
|
54
|
+
# @example password = Telly::main(parse_opts)
|
55
|
+
def Telly.main(options)
|
56
|
+
# Get pass/fail/skips from junit file
|
57
|
+
results = load_junit_results(options[:junit_file])
|
58
|
+
|
59
|
+
puts "Run results:"
|
60
|
+
puts "#{results[:passes].length} Passing"
|
61
|
+
puts "#{results[:failures].length} Failing or Erroring"
|
62
|
+
puts "#{results[:skips].length} Skipped"
|
63
|
+
|
64
|
+
# Set results in testrail
|
65
|
+
bad_results = set_testrail_results(results, options[:junit_file], options[:testrun_id])
|
66
|
+
|
67
|
+
# Print error messages
|
68
|
+
if not bad_results.empty?
|
69
|
+
puts "Error: There were problems processing these test scripts:"
|
70
|
+
bad_results.each do |test_script, error|
|
71
|
+
puts "#{test_script}:\n\t#{error}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
##################################
|
78
|
+
# TestRail API
|
79
|
+
##################################
|
80
|
+
|
81
|
+
# Load testrail credentials from file
|
82
|
+
#
|
83
|
+
# @return [Hash] Contains testrail_username and testrail_password
|
84
|
+
#
|
85
|
+
# @example password = load_credentials()["testrail_password"]
|
86
|
+
def Telly.load_credentials(credentials_file)
|
87
|
+
begin
|
88
|
+
YAML.load_file(File.expand_path(credentials_file))
|
89
|
+
rescue
|
90
|
+
puts "Error: Could not find #{credentials_file}"
|
91
|
+
puts "Create #{credentials_file} with the following:"
|
92
|
+
puts "testrail_username: your.username\ntestrail_password: yourpassword"
|
93
|
+
|
94
|
+
exit
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Returns a testrail API object that talks to testrail
|
101
|
+
#
|
102
|
+
# @param [Hash] credentials A hash containing at least two keys, testrail_username and testrail_password
|
103
|
+
#
|
104
|
+
# @return [TestRail::APIClient] The API object for talking to TestRail
|
105
|
+
#
|
106
|
+
# @example api = get_testrail_api(load_credentials)
|
107
|
+
def Telly.get_testrail_api(credentials)
|
108
|
+
client = TestRail::APIClient.new(TESTRAIL_URL)
|
109
|
+
client.user = credentials["testrail_username"]
|
110
|
+
client.password = credentials["testrail_password"]
|
111
|
+
|
112
|
+
return client
|
113
|
+
end
|
114
|
+
|
115
|
+
# Sets the results in testrail.
|
116
|
+
# Tests that have testrail API exceptions are kept track of in bad_results
|
117
|
+
#
|
118
|
+
# @param [Hash] results A hash of lists of xml objects from the junit output file.
|
119
|
+
# @param [String] junit_file The path to the junit xml file
|
120
|
+
# Needed for determining the path of the test file in add_failure, etc
|
121
|
+
# @param [String] testrun_id The TestRail test run ID
|
122
|
+
#
|
123
|
+
# @return [Void]
|
124
|
+
#
|
125
|
+
def Telly.set_testrail_results(results, junit_file, testrun_id)
|
126
|
+
credentials = load_credentials(CREDENTIALS_FILE)
|
127
|
+
api = get_testrail_api(credentials)
|
128
|
+
|
129
|
+
# Results that couldn't be set in testrail for some reason
|
130
|
+
bad_results = {}
|
131
|
+
|
132
|
+
# passes
|
133
|
+
results[:passes].each do |junit_result|
|
134
|
+
begin
|
135
|
+
submit_result(api, PASSED, junit_result, junit_file, testrun_id)
|
136
|
+
rescue TestRail::APIError => e
|
137
|
+
bad_results[junit_result[:name]] = e.message
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Failures
|
142
|
+
results[:failures].each do |junit_result|
|
143
|
+
begin
|
144
|
+
submit_result(api, FAILED, junit_result, junit_file, testrun_id)
|
145
|
+
rescue TestRail::APIError => e
|
146
|
+
bad_results[junit_result[:name]] = e.message
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Skips
|
151
|
+
results[:skips].each do |junit_result|
|
152
|
+
begin
|
153
|
+
submit_result(api, BLOCKED, junit_result, junit_file, testrun_id)
|
154
|
+
rescue TestRail::APIError => e
|
155
|
+
bad_results[junit_result[:name]] = e.message
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
return bad_results
|
160
|
+
end
|
161
|
+
|
162
|
+
# Submits a test result to TestRail
|
163
|
+
#
|
164
|
+
# @param [TestRail::APIClient] api TestRail API object
|
165
|
+
# @param [int] status The testrail status to set
|
166
|
+
# @param [Nokogiri::XML::Element] junit_result The nokogiri node that holds the junit result
|
167
|
+
# @param [String] junit_file Path to the junit file the test result originated from
|
168
|
+
# @param [String] testrun_id The testrun ID
|
169
|
+
#
|
170
|
+
# @return [Void]
|
171
|
+
#
|
172
|
+
# @raise [TestRail::APIError] When there is a problem with the API request, testrail raises
|
173
|
+
# this exception. Should be caught for error reporting
|
174
|
+
#
|
175
|
+
# @example submit_result(api, BLOCKED, junit_result, junit_file, testrun_id)
|
176
|
+
def Telly.submit_result(api, status, junit_result, junit_file, testrun_id)
|
177
|
+
test_file_path = beaker_test_path(junit_file, junit_result)
|
178
|
+
|
179
|
+
puts junit_result.class
|
180
|
+
testcase_id = testcase_id_from_beaker_script(test_file_path)
|
181
|
+
|
182
|
+
time_elapsed = make_testrail_time(junit_result[:time])
|
183
|
+
|
184
|
+
# Make appropriate comment for testrail
|
185
|
+
case status
|
186
|
+
when FAILED
|
187
|
+
error_message = junit_result.xpath('./failure').first[:message]
|
188
|
+
testrail_comment = "Failed with message:\n#{error_message}"
|
189
|
+
when BLOCKED
|
190
|
+
skip_message = junit_result.xpath('system-out').first.text
|
191
|
+
testrail_comment = "Skipped with message:\n#{skip_message}"
|
192
|
+
else
|
193
|
+
testrail_comment = "Passed"
|
194
|
+
end
|
195
|
+
|
196
|
+
puts "\nSetting result for test case: #{testcase_id}"
|
197
|
+
puts "Adding comment:\n#{testrail_comment}"
|
198
|
+
|
199
|
+
api.send_post("add_result_for_case/#{testrun_id}/#{testcase_id}",
|
200
|
+
{
|
201
|
+
status_id: status,
|
202
|
+
comment: testrail_comment,
|
203
|
+
elapsed: time_elapsed,
|
204
|
+
}
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# Returns a string that testrail accepts as an elapsed time
|
210
|
+
# Input from beaker is a float in seconds, so it rounds it to the
|
211
|
+
# nearest second, and adds an 's' at the end
|
212
|
+
#
|
213
|
+
# Testrail throws an exception if it gets "0s", so it returns a
|
214
|
+
# minimum of "1s"
|
215
|
+
#
|
216
|
+
# @param [String] seconds_string A string that contains only a number, the elapsed time of a test
|
217
|
+
#
|
218
|
+
# @return [String] The elapsed time of the test run, rounded and with an 's' appended
|
219
|
+
#
|
220
|
+
# @example puts make_testrail_time("2.34") # "2s"
|
221
|
+
def Telly.make_testrail_time(seconds_string)
|
222
|
+
# If time is 0, make it 1
|
223
|
+
rounded_time = [seconds_string.to_f.round, 1].max
|
224
|
+
# Test duration
|
225
|
+
time_elapsed = "#{rounded_time}s"
|
226
|
+
|
227
|
+
return time_elapsed
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
##################################
|
232
|
+
# Junit and Beaker file functions
|
233
|
+
##################################
|
234
|
+
|
235
|
+
# Loads the results of a beaker run.
|
236
|
+
# Returns hash of failures, passes, and skips that each hold a list of
|
237
|
+
# junit xml objects
|
238
|
+
#
|
239
|
+
# @param [String] junit_file Path to a junit xml file
|
240
|
+
#
|
241
|
+
# @return [Hash] A hash containing xml objects for the failures, skips, and passes
|
242
|
+
#
|
243
|
+
# @example load_junit_results("~/junit/latest/beaker_junit.xml")
|
244
|
+
def Telly.load_junit_results(junit_file)
|
245
|
+
junit_doc = Nokogiri::XML(File.read(junit_file))
|
246
|
+
|
247
|
+
failures = junit_doc.xpath('//testcase[failure]')
|
248
|
+
skips = junit_doc.xpath('//testcase[skip]')
|
249
|
+
passes = junit_doc.xpath('//testcase[not(failure) and not(skip)]')
|
250
|
+
|
251
|
+
return {failures: failures, skips: skips, passes: passes}
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
# Extracts the test case id from the test script
|
256
|
+
#
|
257
|
+
# @param [String] beaker_file Path to a beaker test script
|
258
|
+
#
|
259
|
+
# @return [String] The test case ID
|
260
|
+
#
|
261
|
+
# @example testcase_id_from_beaker_script("~/tests/test_the_things.rb") # 1234
|
262
|
+
def Telly.testcase_id_from_beaker_script(beaker_file)
|
263
|
+
# Find first matching line
|
264
|
+
match = File.readlines(beaker_file).map { |line| line.match(TESTCASE_ID_REGEX) }.compact.first
|
265
|
+
|
266
|
+
match[:testrun_id]
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
# Calculates the path to a beaker test file by combining the junit file path
|
271
|
+
# with the test name from the junit results.
|
272
|
+
# Makes the assumption that junit folder that beaker creates will always be
|
273
|
+
# 2 directories up from the beaker script base directory.
|
274
|
+
# TODO somewhat hacky, maybe a config/command line option
|
275
|
+
#
|
276
|
+
# @param [String] junit_file_path Path to a junit xml file
|
277
|
+
# @param [String] junit_result Path to a junit xml file
|
278
|
+
#
|
279
|
+
# @return [String] The path to the beaker script from the junit test result
|
280
|
+
#
|
281
|
+
# @example load_junit_results("~/junit/latest/beaker_junit.xml")
|
282
|
+
def Telly.beaker_test_path(junit_file_path, junit_result)
|
283
|
+
beaker_folder_path = junit_result[:classname]
|
284
|
+
test_filename = junit_result[:name]
|
285
|
+
|
286
|
+
File.join(File.dirname(junit_file_path), "../../", beaker_folder_path, test_filename)
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
data/lib/testrail.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#
|
2
|
+
# TestRail API binding for Ruby (API v2, available since TestRail 3.0)
|
3
|
+
#
|
4
|
+
# Learn more:
|
5
|
+
#
|
6
|
+
# http://docs.gurock.com/testrail-api2/start
|
7
|
+
# http://docs.gurock.com/testrail-api2/accessing
|
8
|
+
#
|
9
|
+
# Copyright Gurock Software GmbH. See license.md for details.
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'net/http'
|
13
|
+
require 'net/https'
|
14
|
+
require 'uri'
|
15
|
+
require 'json'
|
16
|
+
|
17
|
+
module TestRail
|
18
|
+
class APIClient
|
19
|
+
@url = ''
|
20
|
+
@user = ''
|
21
|
+
@password = ''
|
22
|
+
|
23
|
+
attr_accessor :user
|
24
|
+
attr_accessor :password
|
25
|
+
|
26
|
+
def initialize(base_url)
|
27
|
+
if !base_url.match(/\/$/)
|
28
|
+
base_url += '/'
|
29
|
+
end
|
30
|
+
@url = base_url + 'index.php?/api/v2/'
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Send Get
|
35
|
+
#
|
36
|
+
# Issues a GET request (read) against the API and returns the result
|
37
|
+
# (as Ruby hash).
|
38
|
+
#
|
39
|
+
# Arguments:
|
40
|
+
#
|
41
|
+
# uri The API method to call including parameters
|
42
|
+
# (e.g. get_case/1)
|
43
|
+
#
|
44
|
+
def send_get(uri)
|
45
|
+
_send_request('GET', uri, nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Send POST
|
50
|
+
#
|
51
|
+
# Issues a POST request (write) against the API and returns the result
|
52
|
+
# (as Ruby hash).
|
53
|
+
#
|
54
|
+
# Arguments:
|
55
|
+
#
|
56
|
+
# uri The API method to call including parameters
|
57
|
+
# (e.g. add_case/1)
|
58
|
+
# data The data to submit as part of the request (as
|
59
|
+
# Ruby hash, strings must be UTF-8 encoded)
|
60
|
+
#
|
61
|
+
def send_post(uri, data)
|
62
|
+
_send_request('POST', uri, data)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def _send_request(method, uri, data)
|
67
|
+
url = URI.parse(@url + uri)
|
68
|
+
if method == 'POST'
|
69
|
+
request = Net::HTTP::Post.new(url.path + '?' + url.query)
|
70
|
+
request.body = JSON.dump(data)
|
71
|
+
else
|
72
|
+
request = Net::HTTP::Get.new(url.path + '?' + url.query)
|
73
|
+
end
|
74
|
+
request.basic_auth(@user, @password)
|
75
|
+
request.add_field('Content-Type', 'application/json')
|
76
|
+
|
77
|
+
conn = Net::HTTP.new(url.host, url.port)
|
78
|
+
if url.scheme == 'https'
|
79
|
+
conn.use_ssl = true
|
80
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
81
|
+
end
|
82
|
+
response = conn.request(request)
|
83
|
+
|
84
|
+
if response.body && !response.body.empty?
|
85
|
+
result = JSON.parse(response.body)
|
86
|
+
else
|
87
|
+
result = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
if response.code != '200'
|
91
|
+
if result && result.key?('error')
|
92
|
+
error = '"' + result['error'] + '"'
|
93
|
+
else
|
94
|
+
error = 'No additional error message received'
|
95
|
+
end
|
96
|
+
raise APIError.new('TestRail API returned HTTP %s (%s)' %
|
97
|
+
[response.code, error])
|
98
|
+
end
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class APIError < StandardError
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: telly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Pinsonault
|
8
|
+
- Puppetlabs
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-12-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: webmock
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: nokogiri
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: Imports beaker results into TestRail test run results
|
57
|
+
email: joe.pinsonault@gmail.com
|
58
|
+
executables:
|
59
|
+
- telly
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- bin/telly
|
64
|
+
- lib/telly.rb
|
65
|
+
- lib/testrail.rb
|
66
|
+
homepage: http://rubygems.org/gems/telly
|
67
|
+
licenses:
|
68
|
+
- Apache
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.4.5
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Imports beaker results into TestRail test run results
|
90
|
+
test_files: []
|