gitlab_quality-test_tooling 0.8.3 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +40 -35
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +8 -1
- data/lib/gitlab_quality/test_tooling/report/report_results.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/results_in_issues.rb +1 -1
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +5 -12
- data/lib/gitlab_quality/test_tooling/test_result/base_test_result.rb +35 -0
- data/lib/gitlab_quality/test_tooling/test_result/j_unit_test_result.rb +42 -0
- data/lib/gitlab_quality/test_tooling/test_result/json_test_result.rb +138 -0
- data/lib/gitlab_quality/test_tooling/test_results/j_unit_test_results.rb +1 -1
- data/lib/gitlab_quality/test_tooling/test_results/json_test_results.rb +1 -1
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +5 -3
- data/lib/gitlab_quality/test_tooling/test_results/test_result.rb +0 -200
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9bcb8c6202edb63313039592fdf1a6c953c0b43db4566d9e84105e31d2029d3
|
4
|
+
data.tar.gz: 27dcc6d668da50a3ec76bd13b8898d61b24bbe2835faeee6fc6dbce684945388
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd3003fced80af3cd8ebafa3495d799eb63756b659b4c6e002c27557ea84beb3ec4bba1bbb08784cf27fe74ab6f4d1c6eb2ed7888fdbf317209683475247310b
|
7
|
+
data.tar.gz: 579e87d66fa4107263220501f4959e3b08b0dbb887224ecb11917a69e5d6774e9cfdd2298d62a20992bc27b7b4be3f561e35574f599a4cbb165230331261e9c5
|
data/Gemfile.lock
CHANGED
@@ -27,7 +27,7 @@ module GitlabQuality
|
|
27
27
|
FAILED_JOB_DESCRIPTION_REGEX = /First happened in #{JOB_URL_REGEX}\./m
|
28
28
|
REPORT_ITEM_REGEX = /^1\. \d{4}-\d{2}-\d{2}: #{JOB_URL_REGEX} \((?<pipeline_url>.+)\)$/
|
29
29
|
NEW_ISSUE_LABELS = Set.new(%w[test failure::new priority::2]).freeze
|
30
|
-
|
30
|
+
IGNORED_FAILURES = [
|
31
31
|
'Net::ReadTimeout',
|
32
32
|
'403 Forbidden - Your account has been blocked'
|
33
33
|
].freeze
|
@@ -54,19 +54,19 @@ module GitlabQuality
|
|
54
54
|
TestResults::Builder.new(files).test_results_per_file do |test_results|
|
55
55
|
puts "=> Reporting #{test_results.count} tests in #{test_results.path}"
|
56
56
|
|
57
|
-
|
57
|
+
systemic_failures = systemic_failures_for_test_results(test_results)
|
58
58
|
|
59
59
|
test_results.each do |test|
|
60
|
-
relate_failure_to_issue(test) if should_report?(test,
|
60
|
+
relate_failure_to_issue(test) if should_report?(test, systemic_failures)
|
61
61
|
end
|
62
62
|
|
63
63
|
test_results.write
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
67
|
+
def systemic_failures_for_test_results(test_results)
|
68
68
|
test_results
|
69
|
-
.flat_map { |test| test.
|
69
|
+
.flat_map { |test| test.failures.map { |failure| failure['message'].lines.first.chomp } }
|
70
70
|
.compact
|
71
71
|
.tally
|
72
72
|
.select { |_e, count| count >= SYSTEMIC_EXCEPTIONS_THRESHOLD }
|
@@ -172,18 +172,16 @@ module GitlabQuality
|
|
172
172
|
end
|
173
173
|
|
174
174
|
def failure_issues(test)
|
175
|
-
|
176
|
-
gitlab.find_issues(options: { state: 'opened', labels: search_labels }).select do |issue|
|
177
|
-
issue_title = issue.title.strip
|
178
|
-
issue_title.include?(test.name) || issue_title.include?(partial_file_path(test.file))
|
179
|
-
end
|
175
|
+
find_issues(test, (base_issue_labels + Set.new(%w[test])).to_a)
|
180
176
|
end
|
181
177
|
|
182
178
|
def full_stacktrace(test)
|
183
|
-
|
184
|
-
|
179
|
+
first_failure = test.failures.first
|
180
|
+
|
181
|
+
if first_failure['message_lines'].empty?
|
182
|
+
first_failure['message']
|
185
183
|
else
|
186
|
-
|
184
|
+
first_failure['message_lines'].join("\n")
|
187
185
|
end
|
188
186
|
end
|
189
187
|
|
@@ -371,7 +369,7 @@ module GitlabQuality
|
|
371
369
|
failure = full_stacktrace(test)
|
372
370
|
return if SCREENSHOT_IGNORED_ERRORS.any? { |e| failure.include?(e) }
|
373
371
|
|
374
|
-
relative_url = gitlab.upload_file(file_fullpath: test.
|
372
|
+
relative_url = gitlab.upload_file(file_fullpath: test.screenshot_image)
|
375
373
|
return unless relative_url
|
376
374
|
|
377
375
|
"### Screenshot\n\n#{relative_url.markdown}"
|
@@ -381,37 +379,44 @@ module GitlabQuality
|
|
381
379
|
#
|
382
380
|
# @return [TrueClass|FalseClass] false if the test was skipped or failed because of a transient error that can be ignored.
|
383
381
|
# Otherwise returns true.
|
384
|
-
def should_report?(test,
|
385
|
-
return false
|
382
|
+
def should_report?(test, systemic_failure_messages)
|
383
|
+
return false unless test.failures?
|
386
384
|
|
387
|
-
puts " => Systemic
|
388
|
-
|
385
|
+
puts " => Systemic failures detected: #{systemic_failure_messages}" if systemic_failure_messages.any?
|
386
|
+
failure_to_ignore = IGNORED_FAILURES + systemic_failure_messages
|
389
387
|
|
390
|
-
|
391
|
-
reason = ignore_failure_reason(test.report['exceptions'], exceptions_to_ignore)
|
388
|
+
reason = ignored_failure_reason(test.failures, failure_to_ignore)
|
392
389
|
|
393
|
-
|
394
|
-
|
390
|
+
if reason
|
391
|
+
puts " => Failure reporting skipped because #{reason}"
|
395
392
|
|
396
|
-
|
397
|
-
|
393
|
+
false
|
394
|
+
else
|
395
|
+
true
|
398
396
|
end
|
399
|
-
|
400
|
-
true
|
401
397
|
end
|
402
398
|
|
403
399
|
# Determine any reason to ignore a failure.
|
404
400
|
#
|
405
|
-
# @param [Array<Hash>]
|
406
|
-
# @
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
401
|
+
# @param [Array<Hash>] failures the failures associated with the failure.
|
402
|
+
# @param [Array<String>] failure_to_ignore the failures messages that should be ignored.
|
403
|
+
# @return [String] the reason to ignore the failures, or `nil` if any failures should not be ignored.
|
404
|
+
def ignored_failure_reason(failures, failure_to_ignore)
|
405
|
+
failures_to_ignore = compute_ignored_failures(failures, failure_to_ignore)
|
406
|
+
return if failures_to_ignore.empty? || failures_to_ignore.size < failures.size
|
407
|
+
|
408
|
+
"the errors included: #{failures_to_ignore.map { |e| "`#{e}`" }.join(', ')}"
|
409
|
+
end
|
412
410
|
|
413
|
-
|
414
|
-
|
411
|
+
# Determine the failures that should be ignored based on a list of exception messages to ignore.
|
412
|
+
#
|
413
|
+
# @param [Array<Hash>] failures the failures associated with the failure.
|
414
|
+
# @param [Array<String>] failure_to_ignore the failures messages that should be ignored.
|
415
|
+
# @return [Array<String>] the exception messages to ignore, or `nil` if any failures should not be ignored.
|
416
|
+
def compute_ignored_failures(failures, failure_to_ignore)
|
417
|
+
failures
|
418
|
+
.filter_map { |e| failure_to_ignore.find { |m| e['message'].include?(m) } }
|
419
|
+
.compact
|
415
420
|
end
|
416
421
|
end
|
417
422
|
end
|
@@ -50,7 +50,7 @@ module GitlabQuality
|
|
50
50
|
def test_file_link(test)
|
51
51
|
path_prefix = test.file.start_with?('qa/') ? 'qa/' : ''
|
52
52
|
|
53
|
-
"[`#{path_prefix}#{test.file}`](#{FILE_BASE_URL}#{path_prefix}#{test.file}
|
53
|
+
"[`#{path_prefix}#{test.file}`](#{FILE_BASE_URL}#{path_prefix}#{test.file}#L#{test.line_number})"
|
54
54
|
end
|
55
55
|
|
56
56
|
def new_issue_labels(_test)
|
@@ -128,6 +128,13 @@ module GitlabQuality
|
|
128
128
|
labels
|
129
129
|
end
|
130
130
|
|
131
|
+
def find_issues(test, labels)
|
132
|
+
gitlab.find_issues(options: { state: 'opened', labels: labels.to_a }).find_all do |issue|
|
133
|
+
issue_title = issue.title.strip
|
134
|
+
issue_title.include?(test.name) || issue_title.include?(partial_file_path(test.file))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
131
138
|
def pipeline_name_label
|
132
139
|
case pipeline
|
133
140
|
when 'production'
|
@@ -40,7 +40,7 @@ module GitlabQuality
|
|
40
40
|
puts "Reporting tests in #{test_results.path}"
|
41
41
|
|
42
42
|
test_results.each do |test|
|
43
|
-
next if test.file.include?('/features/sanity/') || test.skipped
|
43
|
+
next if test.file.include?('/features/sanity/') || test.skipped?
|
44
44
|
|
45
45
|
puts "Reporting test: #{test.file} | #{test.name}\n"
|
46
46
|
|
@@ -59,24 +59,17 @@ module GitlabQuality
|
|
59
59
|
def create_slow_issue(test)
|
60
60
|
puts " => Finding existing issues for slow test '#{test.name}' (run time: #{test.run_time} seconds)..."
|
61
61
|
|
62
|
-
|
62
|
+
issues = find_issues(test, SEARCH_LABELS)
|
63
63
|
|
64
|
-
|
64
|
+
issues.each do |issue|
|
65
|
+
puts " => Existing issue link #{issue['web_url']}"
|
66
|
+
end
|
65
67
|
|
66
|
-
create_issue(test) unless
|
68
|
+
create_issue(test) unless issues.any?
|
67
69
|
rescue MultipleIssuesFound => e
|
68
70
|
warn(e.message)
|
69
71
|
end
|
70
72
|
|
71
|
-
def find_issue(test)
|
72
|
-
search_labels = SEARCH_LABELS
|
73
|
-
|
74
|
-
gitlab.find_issues(options: { state: 'opened', labels: search_labels.to_a }).find do |issue|
|
75
|
-
issue_title = issue.title.strip
|
76
|
-
issue_title.include?(test.name) || issue_title.include?(partial_file_path(test.file))
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
73
|
def should_create_slow_issue?(test)
|
81
74
|
test.run_time > max_duration_for_test(test)
|
82
75
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GitlabQuality
|
4
|
+
module TestTooling
|
5
|
+
module TestResult
|
6
|
+
class BaseTestResult
|
7
|
+
attr_reader :report
|
8
|
+
|
9
|
+
def initialize(report)
|
10
|
+
@report = report
|
11
|
+
end
|
12
|
+
|
13
|
+
def stage
|
14
|
+
@stage ||= file[%r{(?:api|browser_ui)/(?:(?:\d+_)?(\w+))}, 1]
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def file
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def skipped?
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def failures
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GitlabQuality
|
4
|
+
module TestTooling
|
5
|
+
module TestResult
|
6
|
+
class JUnitTestResult < BaseTestResult
|
7
|
+
attr_accessor :testcase # Ignore it for now
|
8
|
+
|
9
|
+
def name
|
10
|
+
report['name']
|
11
|
+
end
|
12
|
+
|
13
|
+
def file
|
14
|
+
report['file'].delete_prefix('./')
|
15
|
+
end
|
16
|
+
|
17
|
+
def skipped?
|
18
|
+
report.search('skipped').any?
|
19
|
+
end
|
20
|
+
|
21
|
+
def failures # rubocop:disable Metrics/AbcSize
|
22
|
+
failures = report.search('failure')
|
23
|
+
return [] if failures.empty?
|
24
|
+
|
25
|
+
failures.map do |exception|
|
26
|
+
trace = exception.content.split("\n").map(&:strip)
|
27
|
+
spec_file_first_index = trace.rindex do |line|
|
28
|
+
line.include?(File.basename(report['file']))
|
29
|
+
end
|
30
|
+
|
31
|
+
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
32
|
+
|
33
|
+
{
|
34
|
+
'message' => "#{exception['type']}: #{exception['message']}",
|
35
|
+
'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GitlabQuality
|
4
|
+
module TestTooling
|
5
|
+
module TestResult
|
6
|
+
class JsonTestResult < BaseTestResult
|
7
|
+
PRIVATE_TOKEN_REGEX = /(private_token=)[\w-]+/
|
8
|
+
|
9
|
+
def name
|
10
|
+
report.fetch('full_description')
|
11
|
+
end
|
12
|
+
|
13
|
+
def file
|
14
|
+
report.fetch('file_path').delete_prefix('./')
|
15
|
+
end
|
16
|
+
|
17
|
+
def status
|
18
|
+
report.fetch('status')
|
19
|
+
end
|
20
|
+
|
21
|
+
def skipped?
|
22
|
+
status == 'pending'
|
23
|
+
end
|
24
|
+
|
25
|
+
def ci_job_url
|
26
|
+
report.fetch('ci_job_url', '')
|
27
|
+
end
|
28
|
+
|
29
|
+
def testcase
|
30
|
+
report.fetch('testcase', '')
|
31
|
+
end
|
32
|
+
|
33
|
+
def testcase=(new_testcase)
|
34
|
+
report['testcase'] = new_testcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def failure_issue
|
38
|
+
report['failure_issue']
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure_issue=(new_failure_issue)
|
42
|
+
report['failure_issue'] = new_failure_issue
|
43
|
+
end
|
44
|
+
|
45
|
+
def quarantine?
|
46
|
+
# The value for 'quarantine' could be nil, a hash, a string,
|
47
|
+
# or true (if the test just has the :quarantine tag)
|
48
|
+
# But any non-nil or false value should means the test is in quarantine
|
49
|
+
!!quarantine
|
50
|
+
end
|
51
|
+
|
52
|
+
def quarantine_type
|
53
|
+
quarantine['type'] if quarantine?
|
54
|
+
end
|
55
|
+
|
56
|
+
def quarantine_issue
|
57
|
+
quarantine['issue'] if quarantine?
|
58
|
+
end
|
59
|
+
|
60
|
+
def screenshot?
|
61
|
+
!!screenshot
|
62
|
+
end
|
63
|
+
|
64
|
+
def screenshot_image
|
65
|
+
screenshot['image'] if screenshot?
|
66
|
+
end
|
67
|
+
|
68
|
+
def product_group
|
69
|
+
report['product_group'].to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def product_group?
|
73
|
+
product_group != ''
|
74
|
+
end
|
75
|
+
|
76
|
+
def feature_category
|
77
|
+
report['feature_category']
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_time
|
81
|
+
report['run_time'].to_f.round(2)
|
82
|
+
end
|
83
|
+
|
84
|
+
def example_id
|
85
|
+
report['id']
|
86
|
+
end
|
87
|
+
|
88
|
+
def line_number
|
89
|
+
report['line_number']
|
90
|
+
end
|
91
|
+
|
92
|
+
def failures # rubocop:disable Metrics/AbcSize
|
93
|
+
@failures ||=
|
94
|
+
report.fetch('exceptions', []).filter_map do |exception|
|
95
|
+
backtrace = exception['backtrace']
|
96
|
+
next unless backtrace.respond_to?(:rindex)
|
97
|
+
|
98
|
+
spec_file_first_index = backtrace.rindex do |line|
|
99
|
+
line.include?(File.basename(report['file_path']))
|
100
|
+
end
|
101
|
+
|
102
|
+
message = redact_private_token(exception['message'])
|
103
|
+
message_lines = Array(exception['message_lines']).map { |line| redact_private_token(line) }
|
104
|
+
|
105
|
+
{
|
106
|
+
'message' => "#{exception['class']}: #{message}",
|
107
|
+
'message_lines' => message_lines,
|
108
|
+
'stacktrace' => "#{format_message_lines(message_lines)}\n#{backtrace.slice(0..spec_file_first_index).join("\n")}",
|
109
|
+
'correlation_id' => exception['correlation_id']
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def failures?
|
115
|
+
failures.any?
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def quarantine
|
121
|
+
report.fetch('quarantine', nil)
|
122
|
+
end
|
123
|
+
|
124
|
+
def screenshot
|
125
|
+
report.fetch('screenshot', nil)
|
126
|
+
end
|
127
|
+
|
128
|
+
def format_message_lines(message_lines)
|
129
|
+
message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
|
130
|
+
end
|
131
|
+
|
132
|
+
def redact_private_token(text)
|
133
|
+
text.gsub(PRIVATE_TOKEN_REGEX, '********')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab_quality-test_tooling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab Quality
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -384,11 +384,13 @@ files:
|
|
384
384
|
- lib/gitlab_quality/test_tooling/system_logs/log_types/rails/graphql_log.rb
|
385
385
|
- lib/gitlab_quality/test_tooling/system_logs/shared_fields.rb
|
386
386
|
- lib/gitlab_quality/test_tooling/system_logs/system_logs_formatter.rb
|
387
|
+
- lib/gitlab_quality/test_tooling/test_result/base_test_result.rb
|
388
|
+
- lib/gitlab_quality/test_tooling/test_result/j_unit_test_result.rb
|
389
|
+
- lib/gitlab_quality/test_tooling/test_result/json_test_result.rb
|
387
390
|
- lib/gitlab_quality/test_tooling/test_results/base_test_results.rb
|
388
391
|
- lib/gitlab_quality/test_tooling/test_results/builder.rb
|
389
392
|
- lib/gitlab_quality/test_tooling/test_results/j_unit_test_results.rb
|
390
393
|
- lib/gitlab_quality/test_tooling/test_results/json_test_results.rb
|
391
|
-
- lib/gitlab_quality/test_tooling/test_results/test_result.rb
|
392
394
|
- lib/gitlab_quality/test_tooling/version.rb
|
393
395
|
- sig/gitlab_quality/test_tooling.rbs
|
394
396
|
homepage: https://gitlab.com/gitlab-org/ruby/gems/gitlab_quality-test_tooling
|
@@ -1,200 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
module GitlabQuality
|
6
|
-
module TestTooling
|
7
|
-
module TestResults
|
8
|
-
class TestResult
|
9
|
-
def self.from_json(report)
|
10
|
-
JsonTestResult.new(report)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.from_junit(report)
|
14
|
-
JUnitTestResult.new(report)
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_accessor :report, :failures
|
18
|
-
|
19
|
-
def initialize(report)
|
20
|
-
self.report = report
|
21
|
-
self.failures = failures_from_exceptions
|
22
|
-
end
|
23
|
-
|
24
|
-
def stage
|
25
|
-
@stage ||= file[%r{(?:api|browser_ui)/(?:(?:\d+_)?(\w+))}, 1]
|
26
|
-
end
|
27
|
-
|
28
|
-
def name
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
def file
|
33
|
-
raise NotImplementedError
|
34
|
-
end
|
35
|
-
|
36
|
-
def skipped
|
37
|
-
raise NotImplementedError
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def failures_from_exceptions
|
43
|
-
raise NotImplementedError
|
44
|
-
end
|
45
|
-
|
46
|
-
class JsonTestResult < TestResult
|
47
|
-
def name
|
48
|
-
report['full_description']
|
49
|
-
end
|
50
|
-
|
51
|
-
def file
|
52
|
-
report['file_path'].delete_prefix('./')
|
53
|
-
end
|
54
|
-
|
55
|
-
def status
|
56
|
-
report['status']
|
57
|
-
end
|
58
|
-
|
59
|
-
def ci_job_url
|
60
|
-
report['ci_job_url']
|
61
|
-
end
|
62
|
-
|
63
|
-
def skipped
|
64
|
-
status == 'pending'
|
65
|
-
end
|
66
|
-
|
67
|
-
def testcase
|
68
|
-
report['testcase']
|
69
|
-
end
|
70
|
-
|
71
|
-
def testcase=(new_testcase)
|
72
|
-
report['testcase'] = new_testcase
|
73
|
-
end
|
74
|
-
|
75
|
-
def failure_issue
|
76
|
-
report['failure_issue']
|
77
|
-
end
|
78
|
-
|
79
|
-
def failure_issue=(new_failure_issue)
|
80
|
-
report['failure_issue'] = new_failure_issue
|
81
|
-
end
|
82
|
-
|
83
|
-
def quarantine?
|
84
|
-
# The value for 'quarantine' could be nil, a hash, a string,
|
85
|
-
# or true (if the test just has the :quarantine tag)
|
86
|
-
# But any non-nil or false value should means the test is in quarantine
|
87
|
-
report['quarantine'].present?
|
88
|
-
end
|
89
|
-
|
90
|
-
def quarantine_type
|
91
|
-
report['quarantine']['type'] if quarantine?
|
92
|
-
end
|
93
|
-
|
94
|
-
def quarantine_issue
|
95
|
-
report['quarantine']['issue'] if quarantine?
|
96
|
-
end
|
97
|
-
|
98
|
-
def screenshot?
|
99
|
-
report['screenshot'].present?
|
100
|
-
end
|
101
|
-
|
102
|
-
def failure_screenshot
|
103
|
-
report['screenshot']['image'] if screenshot?
|
104
|
-
end
|
105
|
-
|
106
|
-
def product_group?
|
107
|
-
report['product_group'].present?
|
108
|
-
end
|
109
|
-
|
110
|
-
def product_group
|
111
|
-
report['product_group']
|
112
|
-
end
|
113
|
-
|
114
|
-
def feature_category
|
115
|
-
report['feature_category']
|
116
|
-
end
|
117
|
-
|
118
|
-
def run_time
|
119
|
-
report['run_time'].to_f.round(2)
|
120
|
-
end
|
121
|
-
|
122
|
-
def example_id
|
123
|
-
report['id']
|
124
|
-
end
|
125
|
-
|
126
|
-
def line_number
|
127
|
-
report['line_number']
|
128
|
-
end
|
129
|
-
|
130
|
-
private
|
131
|
-
|
132
|
-
# rubocop:disable Metrics/AbcSize
|
133
|
-
def failures_from_exceptions
|
134
|
-
return [] unless report.key?('exceptions')
|
135
|
-
|
136
|
-
report['exceptions'].map do |exception|
|
137
|
-
spec_file_first_index = exception['backtrace'].rindex do |line|
|
138
|
-
line.include?(File.basename(report['file_path']))
|
139
|
-
end
|
140
|
-
|
141
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
142
|
-
Array(exception['message_lines']).each { |line| line.gsub!(/(private_token=)([\w-]+)/, '********') }
|
143
|
-
|
144
|
-
{
|
145
|
-
'message' => "#{exception['class']}: #{exception['message']}",
|
146
|
-
'message_lines' => exception['message_lines'],
|
147
|
-
'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}",
|
148
|
-
'correlation_id' => exception['correlation_id']
|
149
|
-
}
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def format_message_lines(message_lines)
|
154
|
-
message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
|
155
|
-
end
|
156
|
-
# rubocop:enable Metrics/AbcSize
|
157
|
-
end
|
158
|
-
|
159
|
-
class JUnitTestResult < TestResult
|
160
|
-
def name
|
161
|
-
report['name']
|
162
|
-
end
|
163
|
-
|
164
|
-
def file
|
165
|
-
report['file'].delete_prefix('./')
|
166
|
-
end
|
167
|
-
|
168
|
-
def skipped
|
169
|
-
report.search('skipped').any?
|
170
|
-
end
|
171
|
-
|
172
|
-
attr_accessor :testcase # Ignore it for now
|
173
|
-
|
174
|
-
private
|
175
|
-
|
176
|
-
# rubocop:disable Metrics/AbcSize
|
177
|
-
def failures_from_exceptions
|
178
|
-
failures = report.search('failure')
|
179
|
-
return [] if failures.empty?
|
180
|
-
|
181
|
-
failures.map do |exception|
|
182
|
-
trace = exception.content.split("\n").map(&:strip)
|
183
|
-
spec_file_first_index = trace.rindex do |line|
|
184
|
-
line.include?(File.basename(report['file']))
|
185
|
-
end
|
186
|
-
|
187
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
188
|
-
|
189
|
-
{
|
190
|
-
'message' => "#{exception['type']}: #{exception['message']}",
|
191
|
-
'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
|
192
|
-
}
|
193
|
-
end
|
194
|
-
end
|
195
|
-
# rubocop:enable Metrics/AbcSize
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|