testrail_rspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 892fde2a276a04d5eb848babfe6f5d55f890bc46
4
+ data.tar.gz: 1367fdb75da3e603bf8ca11ba3bdecf825917d63
5
+ SHA512:
6
+ metadata.gz: f13bbde2db33c79f2f593f971c81190403e5b5d513a5b4d93fc4c143389dadcdd1b775a60a9b7052a4a34c5cf401a5a258e87dba570a27970d021c762ba3f19a
7
+ data.tar.gz: 8a780a95af0e588b3ad66ca7e932a384954d298b72c6be55a005e2e8ed6ccea14f831ebd6d24cad33fae59c2f6a504dcbe21787c6c944a932de1ff3bd206d975
data/.gitignore ADDED
@@ -0,0 +1,39 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ .ruby-version
31
+ .ruby-gemset
32
+ *.bundle
33
+ *.so
34
+ *.o
35
+ *.a
36
+ mkmf.log
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in testrail-rspec.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michal Kubik
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Testrail::Rspec
2
+
3
+ This gem provides custom RSpec formatter allowing to export test results directly to [TestRail][1] instance via their [API][2].
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'testrail_rspec'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install testrail_rspec
20
+
21
+ ## Usage
22
+
23
+ ### Configuration
24
+
25
+ via RSpec.configure in spec_helper.rb - describe it in more details when decided how it is done
26
+
27
+ add to .rspec file this way:
28
+
29
+ $ --format TestrailRSpec
30
+
31
+ or use it from commandline this way:
32
+
33
+ $ rspec spec --format TestrailRSpec
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it ( https://github.com/[my-github-username]/testrail-rspec/fork )
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create a new Pull Request
42
+
43
+ [1]: http://www.gurock.com/testrail/ "TestRail"
44
+ [2]: http://docs.gurock.com/testrail-api2/start "TestRail API"
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,240 @@
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
+ # slightly modified by mkubik
13
+
14
+ require 'net/http'
15
+ require 'net/https'
16
+ require 'uri'
17
+ require 'json'
18
+
19
+ module TestrailRspec
20
+
21
+ AUTOMATED_DESCRIPTION = 'created by automated test suite'
22
+
23
+ STATUS = {
24
+ passed: 1,
25
+ failed: 5,
26
+ skipped: 7,
27
+ pending: 6
28
+ }
29
+
30
+ class Client
31
+
32
+ def initialize(args)
33
+ @projects = nil
34
+ @sections = Hash.new
35
+ @suite = Hash.new
36
+ @suites = Hash.new
37
+
38
+ @client = APIClient.new args[:url]
39
+ %w(user password project).each do |key|
40
+ raise Exception.new("TestRail configuration key :#{key} not set. Cannot continue without it.") if args[key.intern].nil?
41
+ @client.send "#{key}=", args[key.intern] if %w(user password).include? key
42
+ end
43
+ end
44
+
45
+ # ---------------------------------------------------> projects ----------------------------------------------------
46
+ def get_projects
47
+ @projects ||= @client.send_get('get_projects')
48
+ end
49
+
50
+ def add_project(project_name)
51
+ @projects = nil # invalidate cached stuff
52
+ @client.send_post('add_project', {name: project_name,
53
+ announcement: AUTOMATED_DESCRIPTION,
54
+ show_anouncement: true})
55
+ end
56
+
57
+ def get_project(project_id)
58
+ self.get_projects.find { |project| project['id'] == project_id }
59
+ end
60
+
61
+ # ----------------------------------------------------> suites <----------------------------------------------------
62
+ def get_suite(suite_id)
63
+ @suite[suite_id] ||= @client.send_get("get_suite/#{suite_id}")
64
+ end
65
+
66
+ def get_suites(project_id)
67
+ @suites[project_id] ||= @client.send_get("get_suites/#{project_id}")
68
+ end
69
+
70
+ def find_suite_by_name(name, project_id)
71
+ suites = self.get_suites(project_id).select do |suite|
72
+ suite['name'] == name
73
+ end
74
+ puts "TestRail Exporter [WARN] #{suites.size} suites found with name: #{name}. Using first one." if suites.size > 1
75
+ suites.first
76
+ end
77
+
78
+ def create_suite(name, project_id)
79
+ @suites.delete(project_id) # invalidate cached stuff
80
+ puts "TestRail Exporter [INFO] Creating suite: #{name} under project: #{ self.get_project(project_id)['name'] }"
81
+ @client.send_post("add_suite/#{project_id}", { name: name, description: AUTOMATED_DESCRIPTION })
82
+ end
83
+
84
+ def find_or_create_suite(name, project_id)
85
+ self.find_suite_by_name(name, project_id) || self.create_suite(name, project_id)
86
+ end
87
+
88
+ # ---------------------------------------------------> sections <---------------------------------------------------
89
+ def get_sections(suite)
90
+ @sections[suite['id']] ||= @client.send_get("get_sections/#{suite['project_id']}&suite_id=#{suite['id']}")
91
+ end
92
+
93
+ def section_ids_at_depth(suite, depth)
94
+ self.get_sections(suite).select{ |s| s['depth'] == depth }.map{ |s| s['id'] }
95
+ end
96
+
97
+ def find_section(name, suite, parent_id)
98
+ get_sections(suite).select{ |s| s['parent_id'] == parent_id }.find{ |s| s['name'] == name.strip }
99
+ end
100
+
101
+ def find_or_create_section(name, suite, parent, depth)
102
+ parent_id = (depth == 0) ? nil : parent['id']
103
+ self.find_section(name, suite, parent_id) || self.create_section(name, suite, parent_id)
104
+ end
105
+
106
+ def create_section(name, suite, parent_id)
107
+ @sections.delete(suite['id']) # invalidate cached values
108
+ # TODO: check if JSON created have null for nil parent_id
109
+ @client.send_post("add_section/#{suite['project_id']}", { name: name,
110
+ suite_id: suite['id'],
111
+ description: AUTOMATED_DESCRIPTION,
112
+ parent_id: parent_id })
113
+ end
114
+
115
+ # ----------------------------------------------------> cases <-----------------------------------------------------
116
+ def find_or_create_case(title, section, depth)
117
+ self.find_case(title, section, depth) || self.create_case(title, section['id'])
118
+ end
119
+
120
+ def find_case(title, section, depth)
121
+ suite = self.get_suite(section['suite_id'])
122
+ test_cases = @client.send_get("get_cases/#{suite['project_id']}&suite_id=#{suite['id']}")
123
+ test_cases.find do |test_case|
124
+ test_case['title'] == title && test_case['section_id'] == section['id']
125
+ end
126
+ end
127
+
128
+ def create_case(title, section_id)
129
+ @client.send_post("add_case/#{section_id}", { title: title })
130
+ end
131
+
132
+ # ----------------------------------------------------> runs <------------------------------------------------------
133
+ def create_run(suite)
134
+ @client.send_post("add_run/#{suite['project_id']}", { suite_id: suite['id'], name: "#{nice_time_now} - #{suite['name']}", description: 'describe it somehow'})
135
+ end
136
+
137
+ def add_results_for_cases(run_id, results)
138
+ @client.send_post("add_results_for_cases/#{run_id}", { results: results })
139
+ end
140
+
141
+ private
142
+
143
+ def nice_time_now
144
+ Time.now.strftime('%d %b %Y %R:%S %Z')
145
+ end
146
+
147
+ end
148
+
149
+ class APIClient
150
+ @url = ''
151
+ @user = ''
152
+ @password = ''
153
+
154
+ attr_accessor :user
155
+ attr_accessor :password
156
+
157
+ def initialize(base_url)
158
+ if !base_url.match(/\/$/)
159
+ base_url += '/'
160
+ end
161
+ @url = base_url + 'index.php?/api/v2/'
162
+ end
163
+
164
+ #
165
+ # Send Get
166
+ #
167
+ # Issues a GET request (read) against the API and returns the result
168
+ # (as Ruby hash).
169
+ #
170
+ # Arguments:
171
+ #
172
+ # uri The API method to call including parameters
173
+ # (e.g. get_case/1)
174
+ #
175
+ def send_get(uri)
176
+ _send_request('GET', uri, nil)
177
+ end
178
+
179
+ #
180
+ # Send POST
181
+ #
182
+ # Issues a POST request (write) against the API and returns the result
183
+ # (as Ruby hash).
184
+ #
185
+ # Arguments:
186
+ #
187
+ # uri The API method to call including parameters
188
+ # (e.g. add_case/1)
189
+ # data The data to submit as part of the request (as
190
+ # Ruby hash, strings must be UTF-8 encoded)
191
+ #
192
+ def send_post(uri, data)
193
+ _send_request('POST', uri, data)
194
+ end
195
+
196
+ private
197
+ def _send_request(method, uri, data)
198
+ url = URI.parse(@url + uri)
199
+ if method == 'POST'
200
+ request = Net::HTTP::Post.new(url.path + '?' + url.query)
201
+ request.body = JSON.dump(data)
202
+ else
203
+ request = Net::HTTP::Get.new(url.path + '?' + url.query)
204
+ end
205
+ request.basic_auth(@user, @password)
206
+ request.add_field('Content-Type', 'application/json')
207
+
208
+ conn = Net::HTTP.new(url.host, url.port)
209
+ if url.scheme == 'https'
210
+ conn.use_ssl = true
211
+ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
212
+ end
213
+ response = conn.request(request)
214
+
215
+ if response.body && !response.body.empty?
216
+ begin
217
+ result = JSON.parse(response.body)
218
+ rescue JSON::ParserError => e
219
+ raise APIError.new "TestRail API request (#{request.method} #{url}) failed\n#{e.class}: #{e.message}"
220
+ end
221
+ else
222
+ result = {}
223
+ end
224
+
225
+ if response.code != '200'
226
+ if result && result.key?('error')
227
+ error = '"' + result['error'] + '"'
228
+ else
229
+ error = 'No additional error message received'
230
+ end
231
+ raise APIError.new "TestRail API returned HTTP #{response.code} (#{error})"
232
+ end
233
+
234
+ result
235
+ end
236
+ end
237
+
238
+ class APIError < StandardError
239
+ end
240
+ end
@@ -0,0 +1,3 @@
1
+ module TestrailRspec
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,182 @@
1
+ require 'rspec'
2
+ require 'rspec/core/formatters/base_text_formatter'
3
+
4
+ require 'rubytree'
5
+
6
+ require "testrail_rspec/version"
7
+ require "testrail_rspec/client"
8
+
9
+ module TestrailRspec
10
+
11
+ RSpec.configuration.add_setting :testrail_formatter_options, :default => {}
12
+
13
+ class Exporter < RSpec::Core::Formatters::BaseTextFormatter
14
+ RSpec::Core::Formatters.register self, :start, :close, :dump_summary
15
+ # # :example_started, :example_passed,
16
+ # # :example_pending, :example_failed,
17
+ # :dump_failures, :dump_pending
18
+ # # :start_dump
19
+
20
+
21
+ # TODO: after exporter is done and working remove unnecessary overriden methods
22
+
23
+ def initialize(output)
24
+ @options = {}
25
+ @project_id = nil
26
+ super(output)
27
+ end
28
+
29
+ # To start
30
+ def start(notification)
31
+ @options = RSpec.configuration.testrail_formatter_options
32
+ @client = TestrailRspec::Client.new(@options)
33
+ @client.get_projects.each { |project| @project_id = project['id'] if project['name'] == @options[:project] }
34
+
35
+ puts "TestRail Exporter [INFO] Executing #{notification.count} tests. Loaded in #{notification.load_time}"
36
+
37
+ super
38
+ end
39
+
40
+ # Once per example group <-----------------------------------------------------------------------------
41
+ # def example_group_started(notification)
42
+ # groups = []
43
+ # current_group = notification.group
44
+ # until current_group.top_level?
45
+ # groups << current_group
46
+ # current_group = current_group.parent if current_group.parent_groups.size > 1
47
+ # end
48
+ #
49
+ # groups << current_group
50
+ #
51
+ # unless groups[0].examples.empty?
52
+ # groups.reverse.each_with_index do |group, idx|
53
+ # puts (idx == 0 ? "Spec: " : "") + (' ' *2 * idx) + "#{group.description}"
54
+ # end
55
+ # puts (' ' *2 * groups.size) + groups[0].examples.map(&:description).join("\n" + (' ' *2 * groups.size))
56
+ # end
57
+ #
58
+ # super
59
+ # end
60
+
61
+ # Once per example <-----------------------------------------------------------------------------------
62
+ # def example_started(notification)
63
+ # puts " - case: #{notification.example.description}"
64
+ # end
65
+
66
+ # One of these per example <---------------------------------------------------------------------------
67
+ # def example_passed(passed)
68
+ # puts "\tpass: #{passed.example.description}"
69
+ # end
70
+
71
+ # def example_failed(failure)
72
+ # puts "\tfail: #{failure.example.description}"
73
+ # end
74
+ #
75
+ # def example_pending(pending)
76
+ # puts "\tpend: #{pending.example.description}"
77
+ # end
78
+
79
+ # Optionally at any time <------------------------------------------------------------------------------
80
+ # def message(notification)
81
+ # puts "msg notification: #{notification.inspect}"
82
+ # super
83
+ # end
84
+
85
+ # At the end of the suite <-----------------------------------------------------------------------------
86
+ # def stop(notification)
87
+ # puts "stop notification: #{notification.inspect}"
88
+ # end
89
+
90
+ # def start_dump(null_notification)
91
+ # puts "start_dump notification: #{null_notification.inspect}"
92
+ # end
93
+ #
94
+ def dump_pending(notification)
95
+ # puts "dump pend notification: #{notification.inspect}"
96
+ # super
97
+ end
98
+
99
+ def dump_failures(notification)
100
+ # puts "dump fail notification: #{notification.inspect}"
101
+ # super
102
+ end
103
+
104
+ def dump_summary(notification)
105
+ # Create project if it is not present / could do it setting controlled
106
+ if @project_id.nil?
107
+ puts "TestRail Exporter [INFO] Creating project: #{@options[:project]}"
108
+ @project_id = @client.add_project(@options[:project])['id']
109
+ end
110
+
111
+ suites = Hash.new do |h,k|
112
+ h[k] = Tree::TreeNode.new(k, @client.find_or_create_suite(k, @project_id) )
113
+ end
114
+
115
+
116
+ notification.examples.each do |example|
117
+ build_hierarchy_tree!(suites, example)
118
+ end
119
+
120
+ suites.each { |_, suite| update_test_run(suite) }
121
+
122
+ super
123
+ end
124
+
125
+ def close(null_notification)
126
+ # TODO: could close any open connection
127
+ puts "TestRail Exporter [INFO] Closing..."
128
+ super
129
+ end
130
+
131
+ private
132
+
133
+ def get_path_for(node)
134
+ asc_arr = node.is_a?(RSpec::Core::Example) ? [node.description] : []
135
+ parent = (node.respond_to? :parent) ? node.parent : node.example_group
136
+ asc_arr << parent.description
137
+ asc_arr.push(*get_path_for(parent)) unless parent.top_level?
138
+ node.is_a?(RSpec::Core::Example) ? asc_arr.reverse : asc_arr
139
+ end
140
+
141
+ def build_hierarchy_tree!(suites, example)
142
+ path = get_path_for(example)
143
+
144
+ parent_node = suite_node = suites[path.shift]
145
+ path.unshift('Direct cases') unless path.size > 1
146
+
147
+ path.each_with_index do |item, idx|
148
+ child_node = (parent_node.children.map(&:name).include? item) ? parent_node[item] : nil
149
+ if child_node and (idx + 1 == path.size)
150
+ puts "TestRail Exporter [INFO] Second case with same path and name detected:\n\t#{suite_node.content['name']} -> #{path.join(' -> ')}"
151
+ end
152
+
153
+ unless child_node
154
+ child_node = if idx + 1 == path.size
155
+ Tree::TreeNode.new(item, { case: @client.find_or_create_case(item, parent_node.content, idx), result: example })
156
+ else
157
+ Tree::TreeNode.new(item, @client.find_or_create_section(item, suite_node.content, parent_node.content, idx))
158
+ end
159
+ parent_node << child_node
160
+ end
161
+ parent_node = child_node
162
+ end
163
+
164
+ end
165
+
166
+ def update_test_run(suite)
167
+ run_id = @client.create_run(suite.content)['id']
168
+ results = suite.each_leaf.map do |test|
169
+ test_result = test.content[:result].execution_result
170
+ run_time_seconds = test_result.run_time.round(0)
171
+ {
172
+ case_id: test.content[:case]['id'],
173
+ status_id: TestrailRspec::STATUS[test_result.status],
174
+ elapsed: (run_time_seconds == 0) ? nil : "#{run_time_seconds}s"
175
+ }
176
+ end
177
+ @client.add_results_for_cases(run_id, results)
178
+ end
179
+
180
+
181
+ end
182
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'testrail_rspec/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "testrail_rspec"
8
+ spec.version = TestrailRspec::VERSION
9
+ spec.authors = ["Michal Kubik"]
10
+ spec.email = ["michal.kubik@boost.no"]
11
+ spec.summary = %q{RSpec exporter formatter - pushes test run results to TestRail instance.}
12
+ spec.description = %q{Allow exporting RSpect test run results into TestRail server.}
13
+ spec.homepage = "https://github.com/mkubik8080/testrail-rspec"
14
+ spec.license = "MIT"
15
+
16
+ spec.add_runtime_dependency "rubytree", "~> 0.9.4"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: testrail_rspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michal Kubik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubytree
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description: Allow exporting RSpect test run results into TestRail server.
56
+ email:
57
+ - michal.kubik@boost.no
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/testrail_rspec.rb
68
+ - lib/testrail_rspec/client.rb
69
+ - lib/testrail_rspec/version.rb
70
+ - testrail_rspec.gemspec
71
+ homepage: https://github.com/mkubik8080/testrail-rspec
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.4.2
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: RSpec exporter formatter - pushes test run results to TestRail instance.
95
+ test_files: []
96
+ has_rdoc: