telly 0.1.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 +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: []
|