qat-reporter-xray 6.0.0
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/lib/qat/cucumber/core_ext/result.rb +16 -0
- data/lib/qat/cucumber/core_ext.rb +3 -0
- data/lib/qat/formatter/xray/test_ids.rb +96 -0
- data/lib/qat/formatter/xray.rb +186 -0
- data/lib/qat/reporter/xray/config.rb +153 -0
- data/lib/qat/reporter/xray/issue.rb +46 -0
- data/lib/qat/reporter/xray/publisher/base.rb +229 -0
- data/lib/qat/reporter/xray/publisher/cloud.rb +74 -0
- data/lib/qat/reporter/xray/publisher/hosted.rb +56 -0
- data/lib/qat/reporter/xray/publisher.rb +3 -0
- data/lib/qat/reporter/xray/tasks/tests/helpers.rb +112 -0
- data/lib/qat/reporter/xray/tasks/tests/report.rb +47 -0
- data/lib/qat/reporter/xray/tasks/tests.rb +142 -0
- data/lib/qat/reporter/xray/tasks.rb +5 -0
- data/lib/qat/reporter/xray/test.rb +44 -0
- data/lib/qat/reporter/xray/test_execution.rb +79 -0
- data/lib/qat/reporter/xray/version.rb +11 -0
- data/lib/qat/reporter/xray.rb +3 -0
- metadata +193 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'qat/logger'
|
6
|
+
|
7
|
+
module QAT
|
8
|
+
module Reporter
|
9
|
+
class Xray
|
10
|
+
# QAT::Reporter::Xray::Publisher integrator module
|
11
|
+
module Publisher
|
12
|
+
# QAT::Reporter::Xray::Publisher::Base integrator class
|
13
|
+
class Base
|
14
|
+
include QAT::Logger
|
15
|
+
|
16
|
+
attr_reader :base_url, :default_headers, :login_credentials, :default_cloud_api_url, :cloud_xray_api_credentials
|
17
|
+
|
18
|
+
# Initializes Xray Publisher url and login information
|
19
|
+
def initialize
|
20
|
+
@base_url = QAT::Reporter::Xray::Config.jira_url
|
21
|
+
@login_credentials = QAT::Reporter::Xray::Config.login_credentials
|
22
|
+
@default_cloud_api_url = QAT::Reporter::Xray::Config.xray_default_api_url
|
23
|
+
@cloud_xray_api_credentials = QAT::Reporter::Xray::Config.cloud_xray_api_credentials
|
24
|
+
end
|
25
|
+
|
26
|
+
# Creates a Jira issue
|
27
|
+
def create_issue(data)
|
28
|
+
Client.new(base_url).post('/rest/api/2/issue', data.to_json, default_headers)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Get the default headers for Xray ('password' in Xray API is password)
|
33
|
+
def default_headers
|
34
|
+
headers = if QAT::Reporter::Xray::Config.jira_type == 'cloud'
|
35
|
+
auth_headers_jira_cloud
|
36
|
+
else
|
37
|
+
auth_headers
|
38
|
+
end
|
39
|
+
{
|
40
|
+
'Content-Type': 'application/json'
|
41
|
+
}.merge(headers)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
# Authentication header for xray, Basic authentication done with: username, password
|
47
|
+
def auth_headers
|
48
|
+
username = login_credentials[0]
|
49
|
+
password = login_credentials[1]
|
50
|
+
{
|
51
|
+
Authorization: "Basic #{::Base64::encode64("#{username}:#{password}").delete("\n")}"
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Authentication header for jira, Basic authentication done with: username, apiToken
|
56
|
+
def auth_headers_jira_cloud
|
57
|
+
username = login_credentials[0]
|
58
|
+
api_token = login_credentials[2]
|
59
|
+
{
|
60
|
+
Authorization: "Basic #{::Base64::encode64("#{username}:#{api_token}").delete("\n")}"
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
#Gets a zip file from a response and extracts features to 'Feature' folder
|
65
|
+
def extract_feature_files(rsp)
|
66
|
+
Zip::InputStream.open(StringIO.new(rsp)) do |io|
|
67
|
+
while entry = io.get_next_entry
|
68
|
+
entry_path = File.join('features', entry.name)
|
69
|
+
log.info 'Feature ' + entry.name + ' was found, extracting...'
|
70
|
+
entry.extract(File.join(Dir.pwd, entry_path)) unless File.exist?(entry_path)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
#See https://github.com/rubyzip/rubyzip#notice-about-zipinputstream
|
74
|
+
rescue Zip::GPFBit3Error
|
75
|
+
Tempfile.open do |file|
|
76
|
+
File.write(file.path, rsp)
|
77
|
+
|
78
|
+
Zip::File.open(file.path) do |zip_file|
|
79
|
+
# Handle entries one by one
|
80
|
+
zip_file.each do |entry|
|
81
|
+
# Extract to file/directory/symlink
|
82
|
+
log.info 'Feature ' + entry.name + ' was found, extracting...'
|
83
|
+
entry_path = File.join('features', entry.name)
|
84
|
+
entry.extract(entry_path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# REST Base Client implementation
|
91
|
+
class Client
|
92
|
+
include QAT::Logger
|
93
|
+
|
94
|
+
# Service Unavailable Error class
|
95
|
+
class ServiceUnavailableError < StandardError
|
96
|
+
end
|
97
|
+
# Connection Error class
|
98
|
+
class ConnectionError < StandardError
|
99
|
+
end
|
100
|
+
|
101
|
+
# No Connection Error class
|
102
|
+
class NoConnectionFound < StandardError
|
103
|
+
end
|
104
|
+
|
105
|
+
attr_reader :base_uri
|
106
|
+
|
107
|
+
# Returns a new REST Base Client
|
108
|
+
# @return [RestClient::Response]
|
109
|
+
def initialize(base_uri)
|
110
|
+
#sets the ip:port/base_route
|
111
|
+
@base_uri = case base_uri
|
112
|
+
when Hash
|
113
|
+
URI::HTTP.build(base_uri).to_s
|
114
|
+
when URI::HTTP
|
115
|
+
base_uri.to_s
|
116
|
+
when String
|
117
|
+
base_uri
|
118
|
+
else
|
119
|
+
raise ArgumentError.new "Invalid URI class: #{base_uri.class}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
[:put, :post, :get, :delete, :patch].each do |operation|
|
124
|
+
define_method operation do |url, *args|
|
125
|
+
final_url = base_uri + url
|
126
|
+
|
127
|
+
log_request operation, final_url, args
|
128
|
+
begin
|
129
|
+
response = RestClient.method(operation).call(final_url, *args)
|
130
|
+
log_response response
|
131
|
+
validate response
|
132
|
+
rescue RestClient::ExceptionWithResponse => e
|
133
|
+
log.error e.response
|
134
|
+
raise NoConnectionFound.new ('Jira was not found!!!')
|
135
|
+
rescue => exception
|
136
|
+
log.error "#{exception.class} #{exception.message.to_s}"
|
137
|
+
raise NoConnectionFound.new ('Jira was not found!!!')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
# Validates the response and raises a HTTP Error
|
145
|
+
#@param response [RestClient::Response] response
|
146
|
+
def validate(response)
|
147
|
+
error_klass = case response.code
|
148
|
+
when 400 then
|
149
|
+
Error::BadRequest
|
150
|
+
when 401 then
|
151
|
+
Error::Unauthorized
|
152
|
+
when 403 then
|
153
|
+
Error::Forbidden
|
154
|
+
when 404 then
|
155
|
+
Error::NotFound
|
156
|
+
when 405 then
|
157
|
+
Error::MethodNotAllowed
|
158
|
+
when 409 then
|
159
|
+
Error::Conflict
|
160
|
+
when 422 then
|
161
|
+
Error::Unprocessable
|
162
|
+
when 500 then
|
163
|
+
Error::InternalServerError
|
164
|
+
when 502 then
|
165
|
+
Error::BadGateway
|
166
|
+
when 503 then
|
167
|
+
Error::ServiceUnavailable
|
168
|
+
end
|
169
|
+
|
170
|
+
raise error_klass.new response if error_klass
|
171
|
+
response
|
172
|
+
end
|
173
|
+
|
174
|
+
# Logs the request information
|
175
|
+
#@param operation [String] HTTP operation called
|
176
|
+
#@param url [String] target URL
|
177
|
+
#@param args [String] request arguments
|
178
|
+
#@see RestClient#get
|
179
|
+
def log_request(operation, url, args)
|
180
|
+
log.info { "#{operation.upcase}: #{url}" }
|
181
|
+
|
182
|
+
args.each do |options|
|
183
|
+
log_http_options options
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Logs the received response
|
188
|
+
#@param response [RestClient::Response] response
|
189
|
+
def log_response(response)
|
190
|
+
log.info "Response HTTP #{response.code} (#{response.body})"
|
191
|
+
|
192
|
+
log_http_options({ headers: response.headers.to_h,
|
193
|
+
body: response.body }.select { |_, value| !value.nil? })
|
194
|
+
end
|
195
|
+
|
196
|
+
# Logs the request's HTTP options
|
197
|
+
#@param options [Hash|String] http options to log
|
198
|
+
def log_http_options(options)
|
199
|
+
if log.debug?
|
200
|
+
temp = if options.is_a?(String)
|
201
|
+
{ payload: JSON.parse(options) }
|
202
|
+
else
|
203
|
+
options.map do |k, v|
|
204
|
+
if k == :body
|
205
|
+
begin
|
206
|
+
[k, JSON.pretty_generate(JSON.parse(v))]
|
207
|
+
#if body is not JSON by some unknown reason, we still want to print
|
208
|
+
rescue JSON::ParserError
|
209
|
+
[k, v]
|
210
|
+
end
|
211
|
+
else
|
212
|
+
[k, v]
|
213
|
+
end
|
214
|
+
end.to_h
|
215
|
+
end
|
216
|
+
|
217
|
+
temp.each do |key, value|
|
218
|
+
log.debug "#{key.to_s.humanize}:"
|
219
|
+
log.debug value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require 'zip'
|
3
|
+
|
4
|
+
module QAT
|
5
|
+
module Reporter
|
6
|
+
class Xray
|
7
|
+
module Publisher
|
8
|
+
# QAT::Reporter::Xray::Publisher::Cloud integrator class
|
9
|
+
class Cloud < Base
|
10
|
+
|
11
|
+
# Posts the execution json results in Xray
|
12
|
+
def send_execution_results(results)
|
13
|
+
headers = { 'Content-Type': 'application/json' }.merge(auth_token)
|
14
|
+
Client.new(default_cloud_api_url).post('/api/v1/import/execution', results.to_json, headers)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get the Authorization Token based on client_id & client_secret (ONLY FOR CLOUD XRAY)
|
18
|
+
def auth_token
|
19
|
+
return @auth_token if @auth_token
|
20
|
+
|
21
|
+
client_id = cloud_xray_api_credentials[0]
|
22
|
+
client_secret = cloud_xray_api_credentials[1]
|
23
|
+
auth_header_cloud = {
|
24
|
+
client_id: client_id,
|
25
|
+
client_secret: client_secret
|
26
|
+
}
|
27
|
+
|
28
|
+
response = Client.new(default_cloud_api_url).post('/api/v1/authenticate', auth_header_cloud).body
|
29
|
+
bearer = JSON.parse(response)
|
30
|
+
@auth_token = {
|
31
|
+
Authorization: "Bearer #{bearer}"
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Import Cucumber features files as a zip file via API
|
36
|
+
# @param project_key [String] JIRA's project key
|
37
|
+
# @param file_path [String] Cucumber features files' zip file
|
38
|
+
# @see https://confluence.xpand-it.com/display/XRAYCLOUD/Importing+Cucumber+Tests+-+REST
|
39
|
+
def import_cucumber_tests(project_key, file_path, project_id = nil)
|
40
|
+
headers = auth_token.merge({
|
41
|
+
multipart: true,
|
42
|
+
params: {
|
43
|
+
projectKey: project_key,
|
44
|
+
projectId: project_id,
|
45
|
+
source: project_key
|
46
|
+
}
|
47
|
+
})
|
48
|
+
payload = { file: File.new(file_path, 'rb') }
|
49
|
+
|
50
|
+
Client.new(default_cloud_api_url).post('/api/v1/import/feature', payload, headers)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Export Xray test scenarios to a zip file via API
|
54
|
+
# @param keys [String] test scenarios
|
55
|
+
# @param filter [String] project filter
|
56
|
+
# @see https://confluence.xpand-it.com/display/XRAYCLOUD/Exporting+Cucumber+Tests+-+REST
|
57
|
+
def export_test_scenarios(keys, filter)
|
58
|
+
params = {
|
59
|
+
keys: keys,
|
60
|
+
}
|
61
|
+
params[:filter] = filter unless filter == 'nil'
|
62
|
+
|
63
|
+
headers = auth_token.merge(params: params)
|
64
|
+
|
65
|
+
log.info "Exporting features from: #{default_cloud_api_url}/api/v1/export/cucumber"
|
66
|
+
rsp = RestClient.get("#{default_cloud_api_url}/api/v1/export/cucumber", headers)
|
67
|
+
|
68
|
+
extract_feature_files(rsp)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'zip'
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module QAT
|
6
|
+
module Reporter
|
7
|
+
class Xray
|
8
|
+
module Publisher
|
9
|
+
# QAT::Reporter::Xray::Publisher::Hosted integrator class
|
10
|
+
class Hosted < Base
|
11
|
+
|
12
|
+
# Posts the execution json results in Xray
|
13
|
+
# @param results [String] Execution results
|
14
|
+
def send_execution_results(results)
|
15
|
+
Client.new(base_url).post('/rest/raven/1.0/import/execution', results.to_json, default_headers)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Import Cucumber features files as a zip file via API
|
19
|
+
# @param project_key [String] JIRA's project key
|
20
|
+
# @param file_path [String] Cucumber features files' zip file
|
21
|
+
# @see https://confluence.xpand-it.com/display/public/XRAY/Importing+Cucumber+Tests+-+REST
|
22
|
+
def import_cucumber_tests(project_key, file_path)
|
23
|
+
headers = default_headers.merge({
|
24
|
+
multipart: true,
|
25
|
+
params: {
|
26
|
+
projectKey: project_key
|
27
|
+
}
|
28
|
+
})
|
29
|
+
payload = { file: File.new(file_path, 'rb') }
|
30
|
+
|
31
|
+
Client.new(base_url).post('/rest/raven/1.0/import/feature', payload, headers)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Export Xray test scenarios to a zip file via API
|
35
|
+
# @param keys [String] test scenarios
|
36
|
+
# @param filter [String] project filter
|
37
|
+
# @see https://confluence.xpand-it.com/display/public/XRAY/Exporting+Cucumber+Tests+-+REST
|
38
|
+
def export_test_scenarios(keys, filter)
|
39
|
+
params = {
|
40
|
+
keys: keys,
|
41
|
+
fz: true
|
42
|
+
}
|
43
|
+
|
44
|
+
params[:filter] = filter unless filter == 'nil'
|
45
|
+
|
46
|
+
headers = default_headers.merge(params: params)
|
47
|
+
|
48
|
+
rsp = RestClient.get("#{base_url}/rest/raven/1.0/export/test", headers)
|
49
|
+
|
50
|
+
extract_feature_files(rsp)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module QAT
|
2
|
+
module Reporter
|
3
|
+
class Xray
|
4
|
+
# namespace for test ids utility methods and objects
|
5
|
+
module Tests
|
6
|
+
# helper methods for test id manipulation
|
7
|
+
module Helpers
|
8
|
+
# Tags all untagged scenarios present in the test id report
|
9
|
+
#@param report [Tests::Report] test id report
|
10
|
+
def tag_untagged(report)
|
11
|
+
max_test_id = report.max_id
|
12
|
+
untagged = report.untagged
|
13
|
+
|
14
|
+
if untagged.any?
|
15
|
+
files = map_untagged(untagged)
|
16
|
+
|
17
|
+
announce_changes(files)
|
18
|
+
|
19
|
+
update_test_ids(files, max_test_id)
|
20
|
+
else
|
21
|
+
puts "There are no scenarios without test id. Last test id given was '@test##{max_test_id}'."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Returns all files containing untagged scenarios and their respective scenario line
|
28
|
+
#@param untagged [Array] list of untagged scenarios
|
29
|
+
#@return [Array]
|
30
|
+
def map_untagged(untagged)
|
31
|
+
files = {}
|
32
|
+
|
33
|
+
untagged.values.each do |file_colon|
|
34
|
+
file, colon = file_colon.split(':')
|
35
|
+
files[file] ||= []
|
36
|
+
files[file] << colon.to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
files
|
40
|
+
end
|
41
|
+
|
42
|
+
# Announces to STDOUT the files that will be changed (test ids added)
|
43
|
+
#@param files [Array] list of files to change
|
44
|
+
#@see TestIds::Helpers#map_untaged
|
45
|
+
def announce_changes(files)
|
46
|
+
puts "Giving test ids to scenarios:"
|
47
|
+
puts files.to_json({
|
48
|
+
indent: ' ',
|
49
|
+
space: ' ',
|
50
|
+
object_nl: "\n"
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
# Rewrites the untagged files adding the missing test ids
|
55
|
+
#@param files [Array] list of files to change
|
56
|
+
#@param max_test_id [Integer] current max test id
|
57
|
+
def update_test_ids(files, max_test_id)
|
58
|
+
#iterate in file to give test id
|
59
|
+
begin
|
60
|
+
file_lines = []
|
61
|
+
files.each { |file, lines| max_test_id = rewrite_file(file, lines, max_test_id) }
|
62
|
+
rescue
|
63
|
+
path = File.join(Dir.pwd, 'public', 'test_ids_failed.feature')
|
64
|
+
puts "Tag attribution failed! Check '#{path}' for more information!"
|
65
|
+
File.write(path, file_lines.join)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Rewrites the target file in the identified lines.
|
70
|
+
# Returns the max test id after the file update.
|
71
|
+
#@param file [String] file to rewrite
|
72
|
+
#@param lines [Array] lines to edit (add test id)
|
73
|
+
#@param max_test_id [Integer] current max test id
|
74
|
+
#@return [Integer]
|
75
|
+
def rewrite_file(file, lines, max_test_id)
|
76
|
+
norm_lines = lines.map { |line| line - 1 }
|
77
|
+
file_path = File.join(Dir.pwd, file)
|
78
|
+
file_lines = File.readlines(file_path)
|
79
|
+
|
80
|
+
norm_lines.size.times do
|
81
|
+
line = norm_lines.shift
|
82
|
+
puts "Editing file #{file} @ line #{line}."
|
83
|
+
max_test_id = add_tags(file_lines, line, max_test_id)
|
84
|
+
end
|
85
|
+
|
86
|
+
File.write(file_path, file_lines.join)
|
87
|
+
|
88
|
+
max_test_id
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adds the test id tag to the identified line to edit
|
92
|
+
# Returns the max test id after the file update.
|
93
|
+
#@param file_lines [Array] Set of file lines
|
94
|
+
#@param line [Integer] index of line to edit
|
95
|
+
#@param max_test_id [Integer] current max test id
|
96
|
+
#@return [Integer]
|
97
|
+
def add_tags(file_lines, line, max_test_id)
|
98
|
+
if file_lines[line - 1].match(/^\s*@\w+/)
|
99
|
+
file_lines[line - 1] = " #{file_lines[line - 1].strip} @id:#{max_test_id += 1}\n"
|
100
|
+
else
|
101
|
+
file_lines[line] = " @id:#{max_test_id += 1}\n#{file_lines[line]}"
|
102
|
+
end
|
103
|
+
|
104
|
+
max_test_id
|
105
|
+
end
|
106
|
+
|
107
|
+
extend self
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'helpers'
|
2
|
+
|
3
|
+
module QAT
|
4
|
+
module Reporter
|
5
|
+
class Xray
|
6
|
+
# namespace for test ids utility methods and objects
|
7
|
+
module Tests
|
8
|
+
# the test id report wrapper
|
9
|
+
class Report
|
10
|
+
include Helpers
|
11
|
+
|
12
|
+
attr_reader :path, :content
|
13
|
+
|
14
|
+
def initialize(path)
|
15
|
+
@path = path
|
16
|
+
@content = JSON.parse(File.read(path))
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the report max test id
|
20
|
+
def max_id
|
21
|
+
@max_id ||= @content['max']
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the report untagged tests information
|
25
|
+
def untagged
|
26
|
+
@untagged ||= @content['untagged']
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the report test id mapping to scenarios information
|
30
|
+
def mapping
|
31
|
+
@mapping ||= @content['mapping']
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the report duplicate test id information
|
35
|
+
def duplicate
|
36
|
+
@duplicate ||= @content['duplicate']
|
37
|
+
end
|
38
|
+
|
39
|
+
# Tags all untagged scenario with a test id
|
40
|
+
def tag_untagged!
|
41
|
+
tag_untagged(self)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
#encoding: utf-8
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'json'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'awesome_print'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'active_support/core_ext/string/inflections'
|
11
|
+
require_relative '../../../formatter/xray/test_ids'
|
12
|
+
require_relative 'tests/report'
|
13
|
+
require_relative 'tests/helpers'
|
14
|
+
|
15
|
+
namespace :qat do
|
16
|
+
namespace :reporter do
|
17
|
+
namespace :xray do
|
18
|
+
namespace :tests do
|
19
|
+
# Run a rake task by name
|
20
|
+
def run_task!(task_name)
|
21
|
+
begin
|
22
|
+
Rake::Task["qat:reporter:xray:tests:#{task_name}"].invoke
|
23
|
+
rescue SystemExit => exception
|
24
|
+
exitstatus = exception.status
|
25
|
+
@kernel.exit(exitstatus) unless exitstatus == 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'Generates the test id report in JSON'
|
30
|
+
task :report_test_ids do
|
31
|
+
FileUtils.mkdir('public') unless File.exists?(File.join(Dir.pwd, 'public'))
|
32
|
+
ENV['CUCUMBER_OPTS'] = nil
|
33
|
+
Cucumber::Rake::Task.new('test_ids', 'Generates test ids as tags for tests without test id') do |task|
|
34
|
+
task.bundler = false
|
35
|
+
task.fork = false
|
36
|
+
task.cucumber_opts = ['--no-profile',
|
37
|
+
'--dry-run',
|
38
|
+
'--format', 'QAT::Formatter::Xray::TestIds',
|
39
|
+
'--out', 'public/xray_test_ids.json']
|
40
|
+
end.runner.run
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Validates the existing test ids and checks for duplicates'
|
44
|
+
task :validate_test_ids do
|
45
|
+
run_task!('report_test_ids')
|
46
|
+
#read json file
|
47
|
+
file_path = File.realpath(File.join(Dir.pwd, 'public', 'xray_test_ids.json'))
|
48
|
+
report = QAT::Reporter::Xray::Tests::Report.new(file_path)
|
49
|
+
|
50
|
+
exit(1) if report.duplicate.any?
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Generates test ids as tags for tests without test id'
|
54
|
+
task :generate_test_ids do
|
55
|
+
run_task!('report_test_ids')
|
56
|
+
#read json file
|
57
|
+
file_path = File.realpath(File.join(Dir.pwd, 'public', 'xray_test_ids.json'))
|
58
|
+
report = QAT::Reporter::Xray::Tests::Report.new(file_path)
|
59
|
+
|
60
|
+
report.tag_untagged!
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'Generate features zip file to import in Xray'
|
64
|
+
task :zip_features do
|
65
|
+
require 'zip'
|
66
|
+
|
67
|
+
file_mask = File.join('features', '**', '*.feature')
|
68
|
+
feature_files = Dir.glob(file_mask)
|
69
|
+
puts feature_files
|
70
|
+
|
71
|
+
zipfile_name = 'features.zip'
|
72
|
+
|
73
|
+
FileUtils.rm_f(zipfile_name) if File.exists?(zipfile_name)
|
74
|
+
|
75
|
+
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
|
76
|
+
feature_files.each do |file|
|
77
|
+
zipfile.add(file, file)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Validates the import task arguments
|
83
|
+
def validate_import_args(args)
|
84
|
+
[:xray_username, :jira_url, :jira_type, :project_key].each do |key|
|
85
|
+
raise ArgumentError.new "No #{key.to_s.humanize} was provided" unless args[key]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
desc 'Import Cucumber tests to Xray'
|
90
|
+
task :import_features, [:xray_username, :jira_url, :jira_type, :project_key, :file_path] do |_, args|
|
91
|
+
run_task!('generate_test_ids')
|
92
|
+
run_task!('zip_features')
|
93
|
+
|
94
|
+
require 'qat/reporter/xray'
|
95
|
+
|
96
|
+
file_path = args.delete(:file_path) || 'features.zip'
|
97
|
+
|
98
|
+
validate_import_args(args)
|
99
|
+
project_key = args[:project_key] or raise ArgumentError.new 'No project key was provided'
|
100
|
+
login_credentials = [args[:xray_username], ENV['JIRA_PASSWORD']] or raise ArgumentError.new 'No login credentials were provided'
|
101
|
+
jira_type = args[:jira_type] or raise ArgumentError.new 'No jira type key was provided'
|
102
|
+
jira_url = args[:jira_url] or raise ArgumentError.new 'No jira url key was provided'
|
103
|
+
|
104
|
+
QAT::Reporter::Xray.configure do |c|
|
105
|
+
c.project_key = project_key
|
106
|
+
c.login_credentials = login_credentials
|
107
|
+
c.cloud_xray_api_credentials = login_credentials if jira_type.eql? 'cloud'
|
108
|
+
c.jira_type = jira_type
|
109
|
+
c.jira_url = jira_url
|
110
|
+
end
|
111
|
+
|
112
|
+
QAT::Reporter::Xray::Config.publisher.import_cucumber_tests(project_key, file_path)
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'Export Xray test scenarios '
|
116
|
+
task :export_xray_test_scenarios, [:xray_username, :jira_url, :jira_type, :project_key, :keys, :filter] do |_, args|
|
117
|
+
|
118
|
+
require 'qat/reporter/xray'
|
119
|
+
|
120
|
+
project_key = args[:project_key] or raise ArgumentError.new 'No project key was provided'
|
121
|
+
login_credentials = [args[:xray_username], ENV['JIRA_PASSWORD']] or raise ArgumentError.new 'No login credentials were provided'
|
122
|
+
jira_type = args[:jira_type] or raise ArgumentError.new 'No jira type was provided'
|
123
|
+
jira_url = args[:jira_url] or raise ArgumentError.new 'No jira url was provided'
|
124
|
+
xray_export_test_filter = args[:filter] or raise ArgumentError.new 'No filter key was provided'
|
125
|
+
xray_export_test_keys = args[:keys] or raise ArgumentError.new 'No test issue key was provided'
|
126
|
+
|
127
|
+
QAT::Reporter::Xray.configure do |c|
|
128
|
+
c.project_key = project_key
|
129
|
+
c.login_credentials = login_credentials
|
130
|
+
c.cloud_xray_api_credentials = login_credentials if jira_type.eql? 'cloud'
|
131
|
+
c.jira_type = jira_type
|
132
|
+
c.jira_url = jira_url
|
133
|
+
c.xray_export_test_filter = xray_export_test_filter
|
134
|
+
c.xray_export_test_keys = xray_export_test_keys
|
135
|
+
end
|
136
|
+
|
137
|
+
QAT::Reporter::Xray::Config.publisher.export_test_scenarios(xray_export_test_keys, xray_export_test_filter)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|