rpruby 1.2 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.github/workflows/main.yml +16 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/report_portal.yaml.example +12 -0
- data/lib/rpruby/cucumber/formatter.rb +87 -0
- data/lib/rpruby/cucumber/messagereport.rb +224 -0
- data/lib/rpruby/cucumber/parallel_formatter.rb +14 -0
- data/lib/rpruby/cucumber/parallel_report.rb +54 -0
- data/lib/rpruby/cucumber/report.rb +220 -0
- data/lib/rpruby/event_bus.rb +30 -0
- data/lib/rpruby/events/prepare_start_item_request.rb +13 -0
- data/lib/rpruby/http_client.rb +64 -0
- data/lib/rpruby/logging/log4r_outputter.rb +16 -0
- data/lib/rpruby/logging/logger.rb +36 -0
- data/lib/rpruby/logging/logging_appender.rb +18 -0
- data/lib/rpruby/models/item_search_options.rb +26 -0
- data/lib/rpruby/models/test_item.rb +22 -0
- data/lib/rpruby/rspec/formatter.rb +112 -0
- data/lib/rpruby/settings.rb +65 -0
- data/lib/rpruby/tasks.rb +27 -0
- data/lib/rpruby/version.rb +5 -0
- data/lib/rpruby.rb +223 -0
- data/rpruby.gemspec +44 -0
- metadata +38 -3
data/lib/rpruby/tasks.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'pathname'
|
3
|
+
require 'tempfile'
|
4
|
+
require_relative '../rpruby'
|
5
|
+
|
6
|
+
namespace :rpruby do
|
7
|
+
desc 'Start launch in Report Portal and print its id to $stdout (for use with attach_to_launch formatter mode)'
|
8
|
+
task :start_launch do
|
9
|
+
description = ENV['description'] || Rpruby::Settings.instance.description
|
10
|
+
file_to_write_launch_id = ENV['file_for_launch_id'] || Rpruby::Settings.instance.file_with_launch_id
|
11
|
+
file_to_write_launch_id ||= Pathname(Dir.tmpdir) + 'rp_launch_id.tmp'
|
12
|
+
launch_id = Rpruby.start_launch(description)
|
13
|
+
File.write(file_to_write_launch_id, launch_id)
|
14
|
+
puts launch_id
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Finish launch in Report Portal (for use with attach_to_launch formatter mode)'
|
18
|
+
task :finish_launch do
|
19
|
+
launch_id = ENV['launch_id'] || Rpruby::Settings.instance.launch_id
|
20
|
+
file_with_launch_id = ENV['file_with_launch_id'] || Rpruby::Settings.instance.file_with_launch_id
|
21
|
+
puts "Launch id isn't provided. Provide it either via RP_LAUNCH_ID or RP_FILE_WITH_LAUNCH_ID environment variables" if !launch_id && !file_with_launch_id
|
22
|
+
puts 'Both RP_LAUNCH_ID and RP_FILE_WITH_LAUNCH_ID are provided via environment variables' if launch_id && file_with_launch_id
|
23
|
+
Rpruby.launch_id = launch_id || File.read(file_with_launch_id)
|
24
|
+
Rpruby.close_child_items(nil)
|
25
|
+
Rpruby.finish_launch
|
26
|
+
end
|
27
|
+
end
|
data/lib/rpruby.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'base64'
|
3
|
+
require 'cgi'
|
4
|
+
require 'http'
|
5
|
+
require 'json'
|
6
|
+
require 'mime/types'
|
7
|
+
require 'pathname'
|
8
|
+
require 'tempfile'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
require_relative 'rpruby/event_bus'
|
12
|
+
require_relative 'rpruby/models/item_search_options'
|
13
|
+
require_relative 'rpruby/models/test_item'
|
14
|
+
require_relative 'rpruby/settings'
|
15
|
+
require_relative 'rpruby/http_client'
|
16
|
+
require_relative "rpruby/version"
|
17
|
+
|
18
|
+
module Rpruby
|
19
|
+
class Error < StandardError; end
|
20
|
+
LOG_LEVELS = { error: 'ERROR', warn: 'WARN', info: 'INFO', debug: 'DEBUG', trace: 'TRACE', fatal: 'FATAL', unknown: 'UNKNOWN' }.freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_accessor :launch_id, :current_scenario
|
24
|
+
|
25
|
+
def now
|
26
|
+
(current_time.to_f * 1000).to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def status_to_level(status)
|
30
|
+
case status
|
31
|
+
when :passed
|
32
|
+
LOG_LEVELS[:info]
|
33
|
+
when :failed, :undefined, :pending, :error
|
34
|
+
LOG_LEVELS[:error]
|
35
|
+
when :skipped
|
36
|
+
LOG_LEVELS[:warn]
|
37
|
+
else
|
38
|
+
LOG_LEVELS.fetch(status, LOG_LEVELS[:info])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_launch(description, start_time = now)
|
43
|
+
required_data = { name: Settings.instance.launch, start_time: start_time, description:
|
44
|
+
description, mode: Settings.instance.launch_mode }
|
45
|
+
data = prepare_options(required_data, Settings.instance)
|
46
|
+
@launch_id = send_request(:post, 'launch', json: data)['id']
|
47
|
+
end
|
48
|
+
|
49
|
+
def finish_launch(end_time = now)
|
50
|
+
data = { end_time: end_time }
|
51
|
+
send_request(:put, "launch/#{@launch_id}/finish", json: data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_item(item_node)
|
55
|
+
path = 'item'
|
56
|
+
path += "/#{item_node.parent.content.id}" unless item_node.parent&.is_root?
|
57
|
+
item = item_node.content
|
58
|
+
data = { start_time: item.start_time, name: item.name[0, 255], type: item.type.to_s, launch_id: @launch_id, description: item.description }
|
59
|
+
data[:tags] = item.tags unless item.tags.empty?
|
60
|
+
event_bus.broadcast(:prepare_start_item_request, request_data: data)
|
61
|
+
send_request(:post, path, json: data)['id']
|
62
|
+
end
|
63
|
+
|
64
|
+
def finish_item(item, status = nil, end_time = nil, force_issue = nil)
|
65
|
+
unless item.nil? || item.id.nil? || item.closed
|
66
|
+
data = { end_time: end_time.nil? ? now : end_time }
|
67
|
+
data[:status] = status unless status.nil?
|
68
|
+
if force_issue && status != :passed # TODO: check for :passed status is probably not needed
|
69
|
+
data[:issue] = { issue_type: 'AUTOMATION_BUG', comment: force_issue.to_s }
|
70
|
+
elsif status == :skipped
|
71
|
+
data[:issue] = { issue_type: 'NOT_ISSUE' }
|
72
|
+
end
|
73
|
+
send_request(:put, "item/#{item.id}", json: data)
|
74
|
+
item.closed = true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: implement force finish
|
79
|
+
|
80
|
+
def send_log(status, message, time)
|
81
|
+
unless @current_scenario.nil? || @current_scenario.closed # it can be nil if scenario outline in expand mode is executed
|
82
|
+
data = { item_id: @current_scenario.id, time: time, level: status_to_level(status), message: message.to_s }
|
83
|
+
send_request(:post, 'log', json: data)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_file(status, path_or_src, label = nil, time = now, mime_type = 'image/png')
|
88
|
+
str_without_nils = path_or_src.to_s.gsub("\0", '') # file? does not allow NULLs inside the string
|
89
|
+
if File.file?(str_without_nils)
|
90
|
+
send_file_from_path(status, path_or_src, label, time, mime_type)
|
91
|
+
else
|
92
|
+
if mime_type =~ /;base64$/
|
93
|
+
mime_type = mime_type[0..-8]
|
94
|
+
path_or_src = Base64.decode64(path_or_src)
|
95
|
+
end
|
96
|
+
extension = ".#{MIME::Types[mime_type].first.extensions.first}"
|
97
|
+
Tempfile.open(['report_portal', extension]) do |tempfile|
|
98
|
+
tempfile.binmode
|
99
|
+
tempfile.write(path_or_src)
|
100
|
+
tempfile.rewind
|
101
|
+
send_file_from_path(status, tempfile.path, label, time, mime_type)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @option options [Hash] options, see ReportPortal::ItemSearchOptions
|
107
|
+
def get_items(filter_options = {})
|
108
|
+
page_size = 100
|
109
|
+
max_pages = 100
|
110
|
+
all_items = []
|
111
|
+
1.step.each do |page_number|
|
112
|
+
raise 'Too many pages with the results were returned' if page_number > max_pages
|
113
|
+
|
114
|
+
options = ItemSearchOptions.new({ page_size: page_size, page_number: page_number }.merge(filter_options))
|
115
|
+
page_items = send_request(:get, 'item', params: options.query_params)['content'].map do |item_params|
|
116
|
+
TestItem.new(item_params)
|
117
|
+
end
|
118
|
+
all_items += page_items
|
119
|
+
break if page_items.size < page_size
|
120
|
+
end
|
121
|
+
all_items
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param item_ids [Array<String>] an array of items to remove (represented by ids)
|
125
|
+
def delete_items(item_ids)
|
126
|
+
send_request(:delete, 'item', params: { ids: item_ids })
|
127
|
+
end
|
128
|
+
|
129
|
+
# needed for parallel formatter
|
130
|
+
def item_id_of(name, parent_node)
|
131
|
+
path = if parent_node.is_root? # folder without parent folder
|
132
|
+
"item?filter.eq.launch=#{@launch_id}&filter.eq.name=#{CGI.escape(name)}&filter.size.path=0"
|
133
|
+
else
|
134
|
+
"item?filter.eq.parent=#{parent_node.content.id}&filter.eq.name=#{CGI.escape(name)}"
|
135
|
+
end
|
136
|
+
data = send_request(:get, path)
|
137
|
+
if data.key? 'content'
|
138
|
+
data['content'].empty? ? nil : data['content'][0]['id']
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# needed for parallel formatter
|
143
|
+
def launch_id_to_number()
|
144
|
+
path = "launch/#{@launch_id}"
|
145
|
+
data = send_request(:get, path)
|
146
|
+
data['id']
|
147
|
+
end
|
148
|
+
|
149
|
+
# needed for parallel formatter
|
150
|
+
def close_child_items(parent_id)
|
151
|
+
path = if parent_id.nil?
|
152
|
+
"item?filter.eq.launchId=#{launch_id_to_number}"
|
153
|
+
else
|
154
|
+
"item?filter.eq.parent=#{parent_id}&page.page=1&page.size=100"
|
155
|
+
end
|
156
|
+
ids = []
|
157
|
+
loop do
|
158
|
+
data = send_request(:get, path)
|
159
|
+
if data.key?('links')
|
160
|
+
link = data['links'].find { |i| i['rel'] == 'next' }
|
161
|
+
url = link.nil? ? nil : link['href']
|
162
|
+
else
|
163
|
+
url = nil
|
164
|
+
end
|
165
|
+
data['content'].each do |i|
|
166
|
+
ids << i['id'] if i['has_childs'] && i['status'] == 'IN_PROGRESS'
|
167
|
+
end
|
168
|
+
break if url.nil?
|
169
|
+
end
|
170
|
+
|
171
|
+
ids.each do |id|
|
172
|
+
close_child_items(id)
|
173
|
+
finish_item(TestItem.new(id: id))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Registers an event. The proc will be called back with the event object.
|
178
|
+
def on_event(name, &proc)
|
179
|
+
event_bus.on(name, &proc)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def send_file_from_path(status, path, label, time, mime_type)
|
185
|
+
File.open(File.realpath(path), 'rb') do |file|
|
186
|
+
filename = File.basename(file)
|
187
|
+
json = [{ level: status_to_level(status), message: label || filename, item_id: @current_scenario.id, time: time, file: { name: filename } }]
|
188
|
+
form = {
|
189
|
+
json_request_part: HTTP::FormData::Part.new(JSON.dump(json), content_type: 'application/json'),
|
190
|
+
binary_part: HTTP::FormData::File.new(file, filename: filename, content_type: MIME::Types[mime_type].first.to_s)
|
191
|
+
}
|
192
|
+
send_request(:post, 'log', form: form)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def send_request(verb, path, options = {})
|
197
|
+
http_client.send_request(verb, path, options)
|
198
|
+
end
|
199
|
+
|
200
|
+
def http_client
|
201
|
+
@http_client ||= HttpClient.new
|
202
|
+
end
|
203
|
+
|
204
|
+
def current_time
|
205
|
+
# `now_without_mock_time` is provided by Timecop and returns a real, not mocked time
|
206
|
+
return Time.now_without_mock_time if Time.respond_to?(:now_without_mock_time)
|
207
|
+
|
208
|
+
Time.now
|
209
|
+
end
|
210
|
+
|
211
|
+
def event_bus
|
212
|
+
@event_bus ||= EventBus.new
|
213
|
+
end
|
214
|
+
|
215
|
+
def prepare_options(data, config = {})
|
216
|
+
if config.attributes
|
217
|
+
data[:attributes] = config.attributes
|
218
|
+
elsif (data[:tags] = config.tags)
|
219
|
+
end
|
220
|
+
data
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/rpruby.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# lib = File.expand_path('lib', __dir__)
|
3
|
+
# $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require_relative "lib/rpruby/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "rpruby"
|
9
|
+
spec.version = Rpruby::VERSION
|
10
|
+
spec.authors = ["Jayachandra Konduru(JC)"]
|
11
|
+
spec.email = ["jayak8390@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = 'ReportPortal Ruby Client'
|
14
|
+
spec.description = 'Cucumber and RSpec clients for EPAM ReportPortal system'
|
15
|
+
spec.homepage = 'https://github.com/jaya-konduru/rpruby'
|
16
|
+
spec.license = "MIT"
|
17
|
+
spec.required_ruby_version = ">= 2.4.0"
|
18
|
+
|
19
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to 'https://mygemserver.com'"
|
20
|
+
|
21
|
+
#spec.metadata["homepage_uri"] = spec.homepage
|
22
|
+
#spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
23
|
+
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
24
|
+
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
29
|
+
end
|
30
|
+
spec.bindir = "bin"
|
31
|
+
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
|
37
|
+
spec.add_dependency('http')
|
38
|
+
spec.add_dependency('mime-types')
|
39
|
+
spec.add_dependency('rubytree')
|
40
|
+
spec.add_development_dependency('rubocop')
|
41
|
+
|
42
|
+
# For more information and examples about making a new gem, checkout our
|
43
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
44
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jayachandra Konduru(JC)
|
@@ -69,10 +69,45 @@ dependencies:
|
|
69
69
|
description: Cucumber and RSpec clients for EPAM ReportPortal system
|
70
70
|
email:
|
71
71
|
- jayak8390@gmail.com
|
72
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- console
|
74
|
+
- setup
|
73
75
|
extensions: []
|
74
76
|
extra_rdoc_files: []
|
75
|
-
files:
|
77
|
+
files:
|
78
|
+
- ".DS_Store"
|
79
|
+
- ".github/workflows/main.yml"
|
80
|
+
- ".gitignore"
|
81
|
+
- ".rspec"
|
82
|
+
- ".rubocop.yml"
|
83
|
+
- CHANGELOG.md
|
84
|
+
- CODE_OF_CONDUCT.md
|
85
|
+
- Gemfile
|
86
|
+
- LICENSE.txt
|
87
|
+
- README.md
|
88
|
+
- Rakefile
|
89
|
+
- bin/console
|
90
|
+
- bin/setup
|
91
|
+
- config/report_portal.yaml.example
|
92
|
+
- lib/rpruby.rb
|
93
|
+
- lib/rpruby/cucumber/formatter.rb
|
94
|
+
- lib/rpruby/cucumber/messagereport.rb
|
95
|
+
- lib/rpruby/cucumber/parallel_formatter.rb
|
96
|
+
- lib/rpruby/cucumber/parallel_report.rb
|
97
|
+
- lib/rpruby/cucumber/report.rb
|
98
|
+
- lib/rpruby/event_bus.rb
|
99
|
+
- lib/rpruby/events/prepare_start_item_request.rb
|
100
|
+
- lib/rpruby/http_client.rb
|
101
|
+
- lib/rpruby/logging/log4r_outputter.rb
|
102
|
+
- lib/rpruby/logging/logger.rb
|
103
|
+
- lib/rpruby/logging/logging_appender.rb
|
104
|
+
- lib/rpruby/models/item_search_options.rb
|
105
|
+
- lib/rpruby/models/test_item.rb
|
106
|
+
- lib/rpruby/rspec/formatter.rb
|
107
|
+
- lib/rpruby/settings.rb
|
108
|
+
- lib/rpruby/tasks.rb
|
109
|
+
- lib/rpruby/version.rb
|
110
|
+
- rpruby.gemspec
|
76
111
|
homepage: https://github.com/jaya-konduru/rpruby
|
77
112
|
licenses:
|
78
113
|
- MIT
|