reportportal 0.7
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/LICENSE +674 -0
- data/LICENSE.LESSER +165 -0
- data/README.md +81 -0
- data/lib/report_portal/cucumber/formatter.rb +67 -0
- data/lib/report_portal/cucumber/json_slurper.rb +177 -0
- data/lib/report_portal/cucumber/parallel_formatter.rb +53 -0
- data/lib/report_portal/cucumber/parallel_report.rb +69 -0
- data/lib/report_portal/cucumber/report.rb +232 -0
- data/lib/report_portal/logging/log4r_outputter.rb +34 -0
- data/lib/report_portal/logging/logger.rb +54 -0
- data/lib/report_portal/logging/logging_appender.rb +36 -0
- data/lib/report_portal/patches/rest_client.rb +63 -0
- data/lib/report_portal/rspec/formatter.rb +130 -0
- data/lib/report_portal/settings.rb +76 -0
- data/lib/report_portal/tasks.rb +44 -0
- data/lib/report_portal/version.rb +21 -0
- data/lib/reportportal.rb +208 -0
- metadata +90 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
# monkey-patch to temporarily solve the issue in https://github.com/rest-client/rest-client/pull/222
|
20
|
+
module RestClient
|
21
|
+
module Payload
|
22
|
+
extend self
|
23
|
+
class Multipart < Base
|
24
|
+
def build_stream(params)
|
25
|
+
content_type = params.delete(:content_type)
|
26
|
+
b = '--' + boundary
|
27
|
+
|
28
|
+
@stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
|
29
|
+
@stream.binmode
|
30
|
+
@stream.write(b + EOL)
|
31
|
+
|
32
|
+
case params
|
33
|
+
when Hash, ParamsArray
|
34
|
+
x = Utils.flatten_params(params)
|
35
|
+
else
|
36
|
+
x = params
|
37
|
+
end
|
38
|
+
|
39
|
+
last_index = x.length - 1
|
40
|
+
x.each_with_index do |a, index|
|
41
|
+
k, v = * a
|
42
|
+
if v.respond_to?(:read) && v.respond_to?(:path)
|
43
|
+
create_file_field(@stream, k, v)
|
44
|
+
else
|
45
|
+
create_regular_field(@stream, k, v, content_type)
|
46
|
+
end
|
47
|
+
@stream.write(EOL + b)
|
48
|
+
@stream.write(EOL) unless last_index == index
|
49
|
+
end
|
50
|
+
@stream.write('--')
|
51
|
+
@stream.write(EOL)
|
52
|
+
@stream.seek(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_regular_field(s, k, v, type = nil)
|
56
|
+
s.write("Content-Disposition: form-data; name=\"#{k}\"")
|
57
|
+
s.write("#{EOL}Content-Type: #{type}#{EOL}") if type
|
58
|
+
s.write(EOL)
|
59
|
+
s.write(v)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'securerandom'
|
20
|
+
require 'tree'
|
21
|
+
require 'rspec/core'
|
22
|
+
|
23
|
+
require_relative '../../reportportal'
|
24
|
+
|
25
|
+
# TODO: Screenshots
|
26
|
+
# TODO: Logs
|
27
|
+
module ReportPortal
|
28
|
+
module RSpec
|
29
|
+
class Formatter
|
30
|
+
MAX_DESCRIPTION_LENGTH = 255
|
31
|
+
MIN_DESCRIPTION_LENGTH = 3
|
32
|
+
|
33
|
+
::RSpec::Core::Formatters.register self, :start, :example_group_started, :example_group_finished,
|
34
|
+
:example_started, :example_passed, :example_failed,
|
35
|
+
:example_pending, :message, :stop
|
36
|
+
|
37
|
+
def initialize(_output)
|
38
|
+
ENV['REPORT_PORTAL_USED'] = 'true'
|
39
|
+
end
|
40
|
+
|
41
|
+
def start(_start_notification)
|
42
|
+
cmd_args = ARGV.map { |arg| (arg.include? 'rp_uuid=')? 'rp_uuid=[FILTERED]' : arg }.join(' ')
|
43
|
+
ReportPortal.start_launch(cmd_args)
|
44
|
+
@root_node = Tree::TreeNode.new(SecureRandom.hex)
|
45
|
+
@current_group_node = @root_node
|
46
|
+
end
|
47
|
+
|
48
|
+
def example_group_started(group_notification)
|
49
|
+
description = group_notification.group.description
|
50
|
+
if description.size < MIN_DESCRIPTION_LENGTH
|
51
|
+
p "Group description should be at least #{MIN_DESCRIPTION_LENGTH} characters ('group_notification': #{group_notification.inspect})"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
item = ReportPortal::TestItem.new(description[0..MAX_DESCRIPTION_LENGTH-1],
|
55
|
+
:TEST,
|
56
|
+
nil,
|
57
|
+
ReportPortal.now,
|
58
|
+
'',
|
59
|
+
false,
|
60
|
+
[])
|
61
|
+
group_node = Tree::TreeNode.new(SecureRandom.hex, item)
|
62
|
+
if group_node.nil?
|
63
|
+
p "Group node is nil for item #{item.inspect}"
|
64
|
+
else
|
65
|
+
@current_group_node << group_node unless @current_group_node.nil? # make @current_group_node parent of group_node
|
66
|
+
@current_group_node = group_node
|
67
|
+
group_node.content.id = ReportPortal.start_item(group_node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def example_group_finished(_group_notification)
|
72
|
+
unless @current_group_node.nil?
|
73
|
+
ReportPortal.finish_item(@current_group_node.content)
|
74
|
+
@current_group_node = @current_group_node.parent
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def example_started(notification)
|
79
|
+
description = notification.example.description
|
80
|
+
if description.size < MIN_DESCRIPTION_LENGTH
|
81
|
+
p "Example description should be at least #{MIN_DESCRIPTION_LENGTH} characters ('notification': #{notification.inspect})"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
ReportPortal.current_scenario = ReportPortal::TestItem.new(description[0..MAX_DESCRIPTION_LENGTH-1],
|
85
|
+
:STEP,
|
86
|
+
nil,
|
87
|
+
ReportPortal.now,
|
88
|
+
'',
|
89
|
+
false,
|
90
|
+
[])
|
91
|
+
example_node = Tree::TreeNode.new(SecureRandom.hex, ReportPortal.current_scenario)
|
92
|
+
if example_node.nil?
|
93
|
+
p "Example node is nil for scenario #{ReportPortal.current_scenario.inspect}"
|
94
|
+
else
|
95
|
+
@current_group_node << example_node
|
96
|
+
example_node.content.id = ReportPortal.start_item(example_node)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def example_passed(_notification)
|
101
|
+
ReportPortal.finish_item(ReportPortal.current_scenario, :passed) unless ReportPortal.current_scenario.nil?
|
102
|
+
ReportPortal.current_scenario = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def example_failed(notification)
|
106
|
+
exception = notification.exception
|
107
|
+
ReportPortal.send_log(:failed, %(#{exception.class}: #{exception.message}\n\nStacktrace: #{notification.formatted_backtrace.join("\n")}), ReportPortal.now)
|
108
|
+
ReportPortal.finish_item(ReportPortal.current_scenario, :failed) unless ReportPortal.current_scenario.nil?
|
109
|
+
ReportPortal.current_scenario = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def example_pending(_notification)
|
113
|
+
ReportPortal.finish_item(ReportPortal.current_scenario, :skipped) unless ReportPortal.current_scenario.nil?
|
114
|
+
ReportPortal.current_scenario = nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def message(notification)
|
118
|
+
if notification.message.respond_to?(:read)
|
119
|
+
ReportPortal.send_file(:passed, notification.message)
|
120
|
+
else
|
121
|
+
ReportPortal.send_log(:passed, notification.message, ReportPortal.now)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def stop(_notification)
|
126
|
+
ReportPortal.finish_launch
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'yaml'
|
20
|
+
require 'singleton'
|
21
|
+
|
22
|
+
module ReportPortal
|
23
|
+
class Settings
|
24
|
+
include Singleton
|
25
|
+
|
26
|
+
PREFIX = 'rp_'
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
filename = ENV.fetch("#{PREFIX}config") do
|
30
|
+
glob = Dir.glob('{,.config/,config/}report{,-,_}portal{.yml,.yaml}')
|
31
|
+
p "Multiple configuration files found for ReportPortal. Using the first one: #{glob.first}" if glob.size > 1
|
32
|
+
glob.first
|
33
|
+
end
|
34
|
+
|
35
|
+
@properties = filename.nil? ? {} : YAML.load_file(filename)
|
36
|
+
keys = {
|
37
|
+
'uuid' => true,
|
38
|
+
'endpoint' => true,
|
39
|
+
'project' => true,
|
40
|
+
'launch' => true,
|
41
|
+
'description' => false,
|
42
|
+
'tags' => false,
|
43
|
+
'is_debug' => false,
|
44
|
+
'disable_ssl_verification' => false,
|
45
|
+
# for parallel execution only
|
46
|
+
'use_standard_logger' => false,
|
47
|
+
'launch_id' => false,
|
48
|
+
'file_with_launch_id' => false,
|
49
|
+
}
|
50
|
+
|
51
|
+
keys.each do |key, is_required|
|
52
|
+
define_singleton_method(key.to_sym) { setting(key) }
|
53
|
+
fail "ReportPortal: Define environment variable '#{PREFIX}#{key}' or key #{key} in the configuration YAML file" if is_required && public_send(key).nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def launch_mode
|
58
|
+
is_debug ? 'DEBUG' : 'DEFAULT'
|
59
|
+
end
|
60
|
+
|
61
|
+
def formatter_modes
|
62
|
+
setting('formatter_modes') || []
|
63
|
+
end
|
64
|
+
|
65
|
+
def project_url
|
66
|
+
"#{endpoint}/#{project}"
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def setting(key)
|
72
|
+
pkey = PREFIX + key
|
73
|
+
ENV.key?(pkey) ? YAML.load(ENV[pkey]) : @properties[key]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'rake'
|
20
|
+
require 'pathname'
|
21
|
+
require 'tempfile'
|
22
|
+
require_relative '../reportportal'
|
23
|
+
|
24
|
+
namespace :reportportal do
|
25
|
+
desc 'Start launch in Report Portal and print its id to $stdout (needed for use with ReportPortal::Cucumber::AttachToLaunchFormatter)'
|
26
|
+
task :start_launch do
|
27
|
+
description = ENV['description']
|
28
|
+
file_to_write_launch_id = ENV.fetch('file_for_launch_id') { Pathname(Dir.tmpdir) + 'rp_launch_id.tmp' }
|
29
|
+
launch_id = ReportPortal.start_launch(description)
|
30
|
+
File.write(file_to_write_launch_id, launch_id)
|
31
|
+
puts launch_id
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Finish launch in Report Portal (needed for use with ReportPortal::Cucumber::AttachToLaunchFormatter)'
|
35
|
+
task :finish_launch do
|
36
|
+
launch_id = ENV['launch_id']
|
37
|
+
file_with_launch_id = ENV['file_with_launch_id']
|
38
|
+
puts "Launch id isn't provided. Provide it either via launch_id or file_with_launch_id environment variables" if !launch_id && !file_with_launch_id
|
39
|
+
puts "Both launch_id and file_with_launch_id are present in environment variables" if launch_id && file_with_launch_id
|
40
|
+
ReportPortal.launch_id = launch_id || File.read(file_with_launch_id)
|
41
|
+
ReportPortal.close_child_items(nil)
|
42
|
+
ReportPortal.finish_launch
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module ReportPortal
|
20
|
+
VERSION = '0.7'
|
21
|
+
end
|
data/lib/reportportal.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# Copyright 2015 EPAM Systems
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# This file is part of Report Portal.
|
5
|
+
#
|
6
|
+
# Report Portal is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# ReportPortal is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
17
|
+
# along with Report Portal. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
require 'rest_client'
|
21
|
+
require 'uri'
|
22
|
+
require 'pathname'
|
23
|
+
require 'tempfile'
|
24
|
+
|
25
|
+
require_relative 'report_portal/settings'
|
26
|
+
require_relative 'report_portal/patches/rest_client'
|
27
|
+
|
28
|
+
module ReportPortal
|
29
|
+
TestItem = Struct.new(:name, :type, :id, :start_time, :description, :closed, :tags)
|
30
|
+
LOG_LEVELS = { error: 'ERROR', warn: 'WARN', info: 'INFO', debug: 'DEBUG', trace: 'TRACE', fatal: 'FATAL', unknown: 'UNKNOWN' }
|
31
|
+
|
32
|
+
@response_handler = proc do |response, request, result, &block|
|
33
|
+
if (200..207).include? response.code
|
34
|
+
response
|
35
|
+
else
|
36
|
+
p "ReportPortal API returned #{response}"
|
37
|
+
p "Offending request method/URL: #{request.args[:method].upcase} #{request.args[:url]}"
|
38
|
+
p "Offending request payload: #{request.args[:payload]}}"
|
39
|
+
response.return!(request, result, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
attr_accessor :launch_id, :current_scenario
|
45
|
+
|
46
|
+
def now
|
47
|
+
(Time.now.to_f * 1000).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def status_to_level(status)
|
51
|
+
case status
|
52
|
+
when :passed
|
53
|
+
LOG_LEVELS[:info]
|
54
|
+
when :failed, :undefined, :pending, :error
|
55
|
+
LOG_LEVELS[:error]
|
56
|
+
when :skipped
|
57
|
+
LOG_LEVELS[:warn]
|
58
|
+
else
|
59
|
+
LOG_LEVELS.fetch(status, LOG_LEVELS[:info])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def start_launch(description, start_time = now)
|
64
|
+
url = "#{Settings.instance.project_url}/launch"
|
65
|
+
data = { name: Settings.instance.launch, start_time: start_time, tags: Settings.instance.tags, description: description, mode: Settings.instance.launch_mode }
|
66
|
+
@launch_id = do_request(url) do |resource|
|
67
|
+
JSON.parse(resource.post(data.to_json, content_type: :json, &@response_handler))['id']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def finish_launch(end_time = now)
|
72
|
+
url = "#{Settings.instance.project_url}/launch/#{@launch_id}/finish"
|
73
|
+
data = { end_time: end_time }
|
74
|
+
do_request(url) do |resource|
|
75
|
+
resource.put data.to_json, content_type: :json, &@response_handler
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def start_item(item_node)
|
80
|
+
url = "#{Settings.instance.project_url}/item"
|
81
|
+
url += "/#{item_node.parent.content.id}" unless item_node.parent && item_node.parent.is_root?
|
82
|
+
item = item_node.content
|
83
|
+
data = { start_time: item.start_time, name: item.name[0, 255], type: item.type.to_s, launch_id: @launch_id, description: item.description }
|
84
|
+
data[:tags] = item.tags unless item.tags.empty?
|
85
|
+
do_request(url) do |resource|
|
86
|
+
JSON.parse(resource.post(data.to_json, content_type: :json, &@response_handler))['id']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def finish_item(item, status = nil, end_time = nil, force_issue = nil)
|
91
|
+
unless item.nil? || item.id.nil? || item.closed
|
92
|
+
url = "#{Settings.instance.project_url}/item/#{item.id}"
|
93
|
+
data = { end_time: end_time.nil? ? now : end_time }
|
94
|
+
data[:status] = status unless status.nil?
|
95
|
+
if force_issue && status != :passed # TODO: check for :passed status is probably not needed
|
96
|
+
data[:issue] = { issue_type: 'AUTOMATION_BUG', comment: force_issue.to_s }
|
97
|
+
elsif status == :skipped
|
98
|
+
data[:issue] = { issue_type: 'NOT_ISSUE' }
|
99
|
+
end
|
100
|
+
do_request(url) do |resource|
|
101
|
+
resource.put data.to_json, content_type: :json, &@response_handler
|
102
|
+
end
|
103
|
+
item.closed = true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# TODO: implement force finish
|
108
|
+
|
109
|
+
def send_log(status, message, time)
|
110
|
+
unless @current_scenario.nil? || @current_scenario.closed # it can be nil if scenario outline in expand mode is executed
|
111
|
+
url = "#{Settings.instance.project_url}/log"
|
112
|
+
data = { item_id: @current_scenario.id, time: time, level: status_to_level(status), message: message.to_s }
|
113
|
+
do_request(url) do |resource|
|
114
|
+
resource.post(data.to_json, content_type: :json, &@response_handler)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def send_file(status, path, label = nil, time = now, mime_type='image/png')
|
120
|
+
url = "#{Settings.instance.project_url}/log"
|
121
|
+
unless File.file?(path)
|
122
|
+
extension = ".#{MIME::Types[mime_type].first.extensions.first}"
|
123
|
+
temp = Tempfile.open(['file',extension])
|
124
|
+
temp.binmode
|
125
|
+
temp.write(Base64.decode64(path))
|
126
|
+
temp.rewind
|
127
|
+
path = temp
|
128
|
+
end
|
129
|
+
File.open(File.realpath(path), 'rb') do |file|
|
130
|
+
label ||= File.basename(file)
|
131
|
+
json = { level: status_to_level(status), message: label, item_id: @current_scenario.id, time: time, file: { name: File.basename(file) } }
|
132
|
+
data = { :json_request_part => [json].to_json, label => file, :multipart => true, :content_type => 'application/json' }
|
133
|
+
do_request(url) do |resource|
|
134
|
+
resource.post(data, { content_type: 'multipart/form-data' }, &@response_handler)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# needed for parallel formatter
|
140
|
+
def item_id_of(name, parent_node)
|
141
|
+
if parent_node.is_root? # folder without parent folder
|
142
|
+
url = "#{Settings.instance.project_url}/item?filter.eq.launch=#{@launch_id}&filter.eq.name=#{URI.escape(name)}&filter.size.path=0"
|
143
|
+
else
|
144
|
+
url = "#{Settings.instance.project_url}/item?filter.eq.parent=#{parent_node.content.id}&filter.eq.name=#{URI.escape(name)}"
|
145
|
+
end
|
146
|
+
do_request(url) do |resource|
|
147
|
+
data = JSON.parse(resource.get)
|
148
|
+
if data.key? 'content'
|
149
|
+
data['content'].empty? ? nil : data['content'][0]['id']
|
150
|
+
else
|
151
|
+
nil # item isn't started yet
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# needed for parallel formatter
|
157
|
+
def close_child_items(parent_id)
|
158
|
+
if parent_id.nil?
|
159
|
+
url = "#{Settings.instance.project_url}/item?filter.eq.launch=#{@launch_id}&filter.size.path=0&page.page=1&page.size=100"
|
160
|
+
else
|
161
|
+
url = "#{Settings.instance.project_url}/item?filter.eq.parent=#{parent_id}&page.page=1&page.size=100"
|
162
|
+
end
|
163
|
+
ids = []
|
164
|
+
loop do
|
165
|
+
response = do_request(url) { |r| JSON.parse(r.get) }
|
166
|
+
if response.key?('links')
|
167
|
+
link = response['links'].find { |i| i['rel'] == 'next' }
|
168
|
+
url = link.nil? ? nil : link['href']
|
169
|
+
else
|
170
|
+
url = nil
|
171
|
+
end
|
172
|
+
response['content'].each do |i|
|
173
|
+
ids << i['id'] if i['has_childs'] && i['status'] == 'IN_PROGRESS'
|
174
|
+
end
|
175
|
+
break if url.nil?
|
176
|
+
end
|
177
|
+
|
178
|
+
ids.each do |id|
|
179
|
+
close_child_items(id)
|
180
|
+
# temporary, we actually only need the id
|
181
|
+
finish_item(TestItem.new(nil, nil, id, nil, nil, nil, nil))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def create_resource(url)
|
188
|
+
props = { :headers => {:Authorization => "Bearer #{Settings.instance.uuid}"}}
|
189
|
+
verify_ssl = Settings.instance.disable_ssl_verification
|
190
|
+
props[:verify_ssl] = !verify_ssl unless verify_ssl.nil?
|
191
|
+
RestClient::Resource.new url, props
|
192
|
+
end
|
193
|
+
|
194
|
+
def do_request(url)
|
195
|
+
resource = create_resource(url)
|
196
|
+
tries = 3
|
197
|
+
begin
|
198
|
+
yield resource
|
199
|
+
rescue
|
200
|
+
p "Request to #{url} produced an exception: #{$!.class}: #{$!}"
|
201
|
+
$!.backtrace.each { |l| p l }
|
202
|
+
retry unless (tries -= 1).zero?
|
203
|
+
p "Failed to execute request to #{url} after 3 attempts."
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|