reportportal 0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|