testrail_rspec 0.0.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 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: