gitlab-qa 10.3.0 → 12.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.gitlab/changelog_config.yml +13 -0
- data/.gitlab/merge_request_templates/Release.md +13 -34
- data/.gitlab-ci.yml +3 -1
- data/.rubocop.yml +13 -2
- data/.rubocop_todo.yml +57 -97
- data/Dangerfile +1 -5
- data/Gemfile.lock +47 -39
- data/README.md +1 -2
- data/docs/release_process.md +1 -1
- data/docs/running_against_remote_grid.md +42 -3
- data/docs/what_tests_can_be_run.md +5 -2
- data/gitlab-qa.gemspec +5 -3
- data/lib/gitlab/qa/component/base.rb +9 -9
- data/lib/gitlab/qa/component/gitaly.rb +7 -5
- data/lib/gitlab/qa/component/gitaly_cluster.rb +15 -9
- data/lib/gitlab/qa/component/gitlab.rb +48 -21
- data/lib/gitlab/qa/component/mail_hog.rb +1 -0
- data/lib/gitlab/qa/component/praefect.rb +41 -31
- data/lib/gitlab/qa/component/selenoid.rb +14 -7
- data/lib/gitlab/qa/component/specs.rb +11 -5
- data/lib/gitlab/qa/component/staging.rb +4 -4
- data/lib/gitlab/qa/component/telegraf.rb +2 -1
- data/lib/gitlab/qa/docker/engine.rb +6 -3
- data/lib/gitlab/qa/docker/volumes.rb +1 -1
- data/lib/gitlab/qa/release.rb +4 -4
- data/lib/gitlab/qa/runner.rb +10 -3
- data/lib/gitlab/qa/runtime/env.rb +47 -62
- data/lib/gitlab/qa/runtime/logger.rb +1 -1
- data/lib/gitlab/qa/runtime/omnibus_configuration.rb +1 -0
- data/lib/gitlab/qa/runtime/omnibus_configurations/decomposition_single_db.rb +1 -2
- data/lib/gitlab/qa/runtime/omnibus_configurations/object_storage_gcs.rb +2 -1
- data/lib/gitlab/qa/runtime/scenario.rb +1 -5
- data/lib/gitlab/qa/scenario/actable.rb +4 -4
- data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +2 -4
- data/lib/gitlab/qa/scenario/test/instance/deployment_base.rb +2 -1
- data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/group_saml.rb +1 -1
- data/lib/gitlab/qa/scenario/test/integration/ldap.rb +5 -6
- data/lib/gitlab/qa/scenario/test/integration/mtls.rb +20 -6
- data/lib/gitlab/qa/scenario/test/integration/oauth.rb +13 -4
- data/lib/gitlab/qa/scenario/test/integration/praefect.rb +16 -10
- data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +5 -2
- data/lib/gitlab/qa/scenario/test/omnibus/update_from_previous.rb +1 -1
- data/lib/gitlab/qa/scenario/test/omnibus/upgrade.rb +1 -3
- data/lib/gitlab/qa/scenario/test/sanity/version.rb +1 -1
- data/lib/gitlab/qa/support/config_scripts.rb +1 -1
- data/lib/gitlab/qa/support/gitlab_version_info.rb +30 -10
- data/lib/gitlab/qa/support/shell_command.rb +1 -0
- data/lib/gitlab/qa/test_logger.rb +2 -2
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +0 -1
- data/support/data/admin_access_token_seed.rb +5 -1
- data/support/data/license_usage_seed.rb +3 -1
- metadata +16 -45
- data/bin/slack +0 -14
- data/exe/gitlab-qa-report +0 -10
- data/lib/gitlab/qa/report/base_test_results.rb +0 -39
- data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
- data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
- data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
- data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
- data/lib/gitlab/qa/report/json_test_results.rb +0 -29
- data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
- data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
- data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
- data/lib/gitlab/qa/report/report_results.rb +0 -64
- data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
- data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
- data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
- data/lib/gitlab/qa/report/summary_table.rb +0 -43
- data/lib/gitlab/qa/report/test_result.rb +0 -184
- data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
- data/lib/gitlab/qa/reporter.rb +0 -131
- data/lib/gitlab/qa/runtime/omnibus_configurations/packages.rb +0 -17
- data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
- data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
- data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
- data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
- data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
- data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
- data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
- data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
- data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
- data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
@@ -1,126 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module Report
|
6
|
-
# Uses the API to create or update GitLab test result issues with the results of tests from RSpec report files.
|
7
|
-
class ResultsInIssues < ReportAsIssue
|
8
|
-
include ResultsReporterShared
|
9
|
-
|
10
|
-
def initialize(**kwargs)
|
11
|
-
super
|
12
|
-
@issue_type = 'issue'
|
13
|
-
end
|
14
|
-
|
15
|
-
def get_related_issue(testcase, test)
|
16
|
-
issue = find_linked_results_issue_by_iid(testcase, test)
|
17
|
-
is_new = false
|
18
|
-
|
19
|
-
if issue
|
20
|
-
issue = update_issue_title(issue, test) if issue_title_needs_updating?(issue, test)
|
21
|
-
else
|
22
|
-
puts "No valid issue link found"
|
23
|
-
issue = find_or_create_results_issue(test)
|
24
|
-
is_new = true
|
25
|
-
end
|
26
|
-
|
27
|
-
[issue, is_new]
|
28
|
-
end
|
29
|
-
|
30
|
-
def update_issue(issue, test)
|
31
|
-
new_labels = issue_labels(issue)
|
32
|
-
new_labels |= ['Testcase Linked']
|
33
|
-
|
34
|
-
labels_updated = update_labels(issue, test, new_labels)
|
35
|
-
note_posted = note_status(issue, test)
|
36
|
-
|
37
|
-
if labels_updated || note_posted
|
38
|
-
puts "Issue updated: #{issue.web_url}"
|
39
|
-
else
|
40
|
-
puts "Test passed, no results issue update needed."
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def find_linked_results_issue_by_iid(testcase, test)
|
47
|
-
iid = issue_iid_from_testcase(testcase)
|
48
|
-
|
49
|
-
return unless iid
|
50
|
-
|
51
|
-
find_issue_by_iid(iid)
|
52
|
-
end
|
53
|
-
|
54
|
-
def find_or_create_results_issue(test)
|
55
|
-
find_issue(test) || create_issue(test)
|
56
|
-
end
|
57
|
-
|
58
|
-
def issue_iid_from_testcase(testcase)
|
59
|
-
results = testcase.description.partition(TEST_CASE_RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE)
|
60
|
-
|
61
|
-
return puts "No issue link found" unless results
|
62
|
-
|
63
|
-
issue_iid = results.split('/').last
|
64
|
-
|
65
|
-
issue_iid&.to_i
|
66
|
-
end
|
67
|
-
|
68
|
-
def note_status(issue, test)
|
69
|
-
return false if test.skipped
|
70
|
-
return false if test.failures.empty?
|
71
|
-
|
72
|
-
note = note_content(test)
|
73
|
-
|
74
|
-
gitlab.find_issue_discussions(iid: issue.iid).each do |discussion|
|
75
|
-
return gitlab.add_note_to_issue_discussion_as_thread(iid: issue.iid, discussion_id: discussion.id, body: failure_summary) if new_note_matches_discussion?(note, discussion)
|
76
|
-
end
|
77
|
-
|
78
|
-
gitlab.create_issue_note(iid: issue.iid, note: note)
|
79
|
-
|
80
|
-
true
|
81
|
-
end
|
82
|
-
|
83
|
-
def note_content(test)
|
84
|
-
errors = test.failures.each_with_object([]) do |failure, text|
|
85
|
-
text << <<~TEXT
|
86
|
-
Error:
|
87
|
-
```
|
88
|
-
#{failure['message']}
|
89
|
-
```
|
90
|
-
|
91
|
-
Stacktrace:
|
92
|
-
```
|
93
|
-
#{failure['stacktrace']}
|
94
|
-
```
|
95
|
-
TEXT
|
96
|
-
end.join("\n\n")
|
97
|
-
|
98
|
-
"#{failure_summary}\n\n#{errors}"
|
99
|
-
end
|
100
|
-
|
101
|
-
def failure_summary
|
102
|
-
summary = [":x: ~\"#{pipeline}::failed\""]
|
103
|
-
summary << "in job `#{Runtime::Env.ci_job_name}` in #{Runtime::Env.ci_job_url}"
|
104
|
-
summary.join(' ')
|
105
|
-
end
|
106
|
-
|
107
|
-
def new_note_matches_discussion?(note, discussion)
|
108
|
-
note_error = error_and_stack_trace(note)
|
109
|
-
discussion_error = error_and_stack_trace(discussion.notes.first['body'])
|
110
|
-
|
111
|
-
return false if note_error.empty? || discussion_error.empty?
|
112
|
-
|
113
|
-
note_error == discussion_error
|
114
|
-
end
|
115
|
-
|
116
|
-
def error_and_stack_trace(text)
|
117
|
-
text.strip[/Error:(.*)/m, 1].to_s
|
118
|
-
end
|
119
|
-
|
120
|
-
def updated_description(issue, test)
|
121
|
-
new_issue_description(test)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'erb'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
8
|
-
# Uses the API to create or update GitLab test cases with the results of tests from RSpec report files.
|
9
|
-
class ResultsInTestCases < ReportAsIssue
|
10
|
-
attr_reader :issue_type, :gitlab
|
11
|
-
|
12
|
-
include ResultsReporterShared
|
13
|
-
|
14
|
-
def initialize(**kwargs)
|
15
|
-
super
|
16
|
-
@issue_type = 'test_case'
|
17
|
-
end
|
18
|
-
|
19
|
-
def find_or_create_testcase(test)
|
20
|
-
find_testcase(test) || create_issue(test)
|
21
|
-
end
|
22
|
-
|
23
|
-
def add_issue_link_to_testcase(testcase, issue, test)
|
24
|
-
results_section = testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE) ? '' : TEST_CASE_RESULTS_SECTION_TEMPLATE
|
25
|
-
|
26
|
-
gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
|
27
|
-
# We are using test.testcase for the url here instead of testcase.web_url since it has the updated test case path
|
28
|
-
puts "Added results issue #{issue.web_url} link to test case #{test.testcase}"
|
29
|
-
end
|
30
|
-
|
31
|
-
def update_testcase(testcase, test)
|
32
|
-
puts "Test case labels updated." if update_labels(testcase, test)
|
33
|
-
puts "Test case quarantine section updated." if update_quarantine_link(testcase, test)
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def find_testcase(test)
|
39
|
-
testcase = find_testcase_by_iid(test)
|
40
|
-
|
41
|
-
if testcase
|
42
|
-
testcase = update_issue_title(testcase, test) if issue_title_needs_updating?(testcase, test)
|
43
|
-
else
|
44
|
-
testcase = find_issue(test)
|
45
|
-
end
|
46
|
-
|
47
|
-
testcase
|
48
|
-
end
|
49
|
-
|
50
|
-
def find_testcase_by_iid(test)
|
51
|
-
iid = testcase_iid_from_url(test.testcase)
|
52
|
-
|
53
|
-
return unless iid
|
54
|
-
|
55
|
-
find_issue_by_iid(iid)
|
56
|
-
end
|
57
|
-
|
58
|
-
def testcase_iid_from_url(url)
|
59
|
-
return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
|
60
|
-
|
61
|
-
url && url.split('/').last.to_i
|
62
|
-
end
|
63
|
-
|
64
|
-
def new_issue_description(test)
|
65
|
-
quarantine_section = test.quarantine? && test.quarantine_issue ? "\n\n### Quarantine issue\n\n#{test.quarantine_issue}" : ''
|
66
|
-
|
67
|
-
"#{super}#{quarantine_section}\n\n#{execution_graph_section(test)}"
|
68
|
-
end
|
69
|
-
|
70
|
-
def execution_graph_section(test)
|
71
|
-
formatted_title = ERB::Util.url_encode(test.name)
|
72
|
-
|
73
|
-
<<~MKDOWN.strip
|
74
|
-
### Executions
|
75
|
-
|
76
|
-
All Environments:
|
77
|
-
<img src="https://dashboards.quality.gitlab.net/render/d-solo/cW0UMgv7k/spec-health?orgId=1&var-run_type=All&var-name=#{formatted_title}&panelId=4&width=1000&height=500" />
|
78
|
-
MKDOWN
|
79
|
-
end
|
80
|
-
|
81
|
-
def updated_description(testcase, test)
|
82
|
-
historical_results_section = testcase.description.match(/### DO NOT EDIT BELOW THIS LINE[\s\S]+/)
|
83
|
-
|
84
|
-
"#{new_issue_description(test)}\n\n#{historical_results_section}"
|
85
|
-
end
|
86
|
-
|
87
|
-
def issue_title_needs_updating?(testcase, test)
|
88
|
-
super || !testcase.description.include?(execution_graph_section(test)) && !%w[canary production preprod release].include?(pipeline)
|
89
|
-
end
|
90
|
-
|
91
|
-
def quarantine_link_needs_updating?(testcase, test)
|
92
|
-
if test.quarantine? && test.quarantine_issue
|
93
|
-
return false if testcase.description.include?(test.quarantine_issue)
|
94
|
-
else
|
95
|
-
return false unless testcase.description.include?('Quarantine issue')
|
96
|
-
end
|
97
|
-
|
98
|
-
true
|
99
|
-
end
|
100
|
-
|
101
|
-
def update_quarantine_link(testcase, test)
|
102
|
-
return unless quarantine_link_needs_updating?(testcase, test)
|
103
|
-
|
104
|
-
new_description = updated_description(testcase, test)
|
105
|
-
|
106
|
-
gitlab.edit_issue(iid: testcase.iid, options: { description: new_description })
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/enumerable'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
8
|
-
module ResultsReporterShared
|
9
|
-
TEST_CASE_RESULTS_SECTION_TEMPLATE = "\n\n### DO NOT EDIT BELOW THIS LINE\n\nActive and historical test results:"
|
10
|
-
|
11
|
-
def find_issue(test)
|
12
|
-
issues = search_for_issues(test)
|
13
|
-
|
14
|
-
warn(%(Too many #{issue_type}s found with the file path "#{test.file}" and name "#{test.name}")) if issues.many?
|
15
|
-
puts "Found existing #{issue_type}: #{issues.first.web_url}" unless issues.empty?
|
16
|
-
|
17
|
-
issues.first
|
18
|
-
end
|
19
|
-
|
20
|
-
def find_issue_by_iid(iid)
|
21
|
-
issues = gitlab.find_issues(iid: iid) do |issue|
|
22
|
-
issue.state == 'opened' && issue.issue_type == issue_type
|
23
|
-
end
|
24
|
-
|
25
|
-
warn(%(#{issue_type} iid "#{iid}" not valid)) if issues.empty?
|
26
|
-
|
27
|
-
issues.first
|
28
|
-
end
|
29
|
-
|
30
|
-
def issue_title_needs_updating?(issue, test)
|
31
|
-
issue.title.strip != title_from_test(test) && !%w[canary production preprod release].include?(pipeline)
|
32
|
-
end
|
33
|
-
|
34
|
-
def new_issue_labels(test)
|
35
|
-
%w[Quality status::automated]
|
36
|
-
end
|
37
|
-
|
38
|
-
def search_term(test)
|
39
|
-
%("#{partial_file_path(test.file)}" "#{search_safe(test.name)}")
|
40
|
-
end
|
41
|
-
|
42
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
43
|
-
labels = super
|
44
|
-
labels |= new_issue_labels(test).to_set
|
45
|
-
labels.delete_if { |label| label.start_with?("#{pipeline}::") }
|
46
|
-
labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
|
47
|
-
end
|
48
|
-
|
49
|
-
def update_issue_title(issue, test)
|
50
|
-
old_title = issue.title.strip
|
51
|
-
new_title = title_from_test(test)
|
52
|
-
|
53
|
-
warn(%(#{issue_type} title needs to be updated from '#{old_title}' to '#{new_title}'))
|
54
|
-
|
55
|
-
new_description = updated_description(issue, test)
|
56
|
-
|
57
|
-
gitlab.edit_issue(iid: issue.iid, options: { title: new_title, description: new_description })
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def search_for_issues(test)
|
63
|
-
gitlab.find_issues(options: { search: search_term(test) }) do |issue|
|
64
|
-
issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'table_print'
|
5
|
-
|
6
|
-
module Gitlab
|
7
|
-
module QA
|
8
|
-
module Report
|
9
|
-
class SummaryTable
|
10
|
-
def self.create(input_files:)
|
11
|
-
"```\n#{TablePrint::Printer.table_print(collect_results(input_files))}```\n"
|
12
|
-
end
|
13
|
-
|
14
|
-
# rubocop:disable Metrics/AbcSize
|
15
|
-
def self.collect_results(input_files)
|
16
|
-
stage_wise_results = []
|
17
|
-
|
18
|
-
Dir.glob(input_files).each do |report_file|
|
19
|
-
stage_hash = {}
|
20
|
-
stage_hash["Dev Stage"] = File.basename(report_file, ".*").capitalize
|
21
|
-
|
22
|
-
report_stats = Nokogiri::XML(File.open(report_file)).children[0].attributes
|
23
|
-
|
24
|
-
stage_hash["Total"] = report_stats["tests"].value
|
25
|
-
stage_hash["Failures"] = report_stats["failures"].value
|
26
|
-
stage_hash["Errors"] = report_stats["errors"].value
|
27
|
-
stage_hash["Skipped"] = report_stats["skipped"].value
|
28
|
-
stage_hash["Result"] = result_emoji(report_stats)
|
29
|
-
|
30
|
-
stage_wise_results << stage_hash
|
31
|
-
end
|
32
|
-
|
33
|
-
stage_wise_results
|
34
|
-
end
|
35
|
-
# rubocop:enable Metrics/AbcSize
|
36
|
-
|
37
|
-
def self.result_emoji(report_stats)
|
38
|
-
report_stats["failures"].value.to_i.positive? || report_stats["errors"].value.to_i.positive? ? "❌" : "✅"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,184 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
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']
|
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'] if product_group?
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
|
116
|
-
# rubocop:disable Metrics/AbcSize
|
117
|
-
def failures_from_exceptions
|
118
|
-
return [] unless report.key?('exceptions')
|
119
|
-
|
120
|
-
report['exceptions'].map do |exception|
|
121
|
-
spec_file_first_index = exception['backtrace'].rindex do |line|
|
122
|
-
line.include?(File.basename(report['file_path']))
|
123
|
-
end
|
124
|
-
|
125
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
126
|
-
Array(exception['message_lines']).each { |line| line.gsub!(/(private_token=)([\w-]+)/, '********') }
|
127
|
-
|
128
|
-
{
|
129
|
-
'message' => "#{exception['class']}: #{exception['message']}",
|
130
|
-
'message_lines' => exception['message_lines'],
|
131
|
-
'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}",
|
132
|
-
'correlation_id' => exception['correlation_id']
|
133
|
-
}
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def format_message_lines(message_lines)
|
138
|
-
message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
|
139
|
-
end
|
140
|
-
# rubocop:enable Metrics/AbcSize
|
141
|
-
end
|
142
|
-
|
143
|
-
class JUnitTestResult < TestResult
|
144
|
-
def name
|
145
|
-
report['name']
|
146
|
-
end
|
147
|
-
|
148
|
-
def file
|
149
|
-
report['file']
|
150
|
-
end
|
151
|
-
|
152
|
-
def skipped
|
153
|
-
report.search('skipped').any?
|
154
|
-
end
|
155
|
-
|
156
|
-
attr_accessor :testcase # Ignore it for now
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
# rubocop:disable Metrics/AbcSize
|
161
|
-
def failures_from_exceptions
|
162
|
-
failures = report.search('failure')
|
163
|
-
return [] if failures.empty?
|
164
|
-
|
165
|
-
failures.map do |exception|
|
166
|
-
trace = exception.content.split("\n").map(&:strip)
|
167
|
-
spec_file_first_index = trace.rindex do |line|
|
168
|
-
line.include?(File.basename(report['file']))
|
169
|
-
end
|
170
|
-
|
171
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
172
|
-
|
173
|
-
{
|
174
|
-
'message' => "#{exception['type']}: #{exception['message']}",
|
175
|
-
'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
|
176
|
-
}
|
177
|
-
end
|
178
|
-
end
|
179
|
-
# rubocop:enable Metrics/AbcSize
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'json'
|
5
|
-
require 'active_support/core_ext/object/blank'
|
6
|
-
|
7
|
-
module Gitlab
|
8
|
-
module QA
|
9
|
-
module Report
|
10
|
-
class UpdateScreenshotPath
|
11
|
-
def initialize(files:)
|
12
|
-
@files = files
|
13
|
-
end
|
14
|
-
|
15
|
-
REGEX = %r{(?<gitlab_qa_run>gitlab-qa-run-.*?(?=\/))\/(?<gitlab_ce_ee_qa>gitlab-(ee|ce)-qa-.*?(?=\/))}
|
16
|
-
CONTAINER_PATH = File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp').freeze
|
17
|
-
|
18
|
-
def invoke!
|
19
|
-
Dir.glob(@files).each do |rspec_report_file|
|
20
|
-
match_data = rspec_report_file.match(REGEX)
|
21
|
-
artifact_path = "#{match_data[:gitlab_qa_run]}/#{match_data[:gitlab_ce_ee_qa]}"
|
22
|
-
|
23
|
-
xml_report = rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
|
24
|
-
|
25
|
-
File.write(rspec_report_file, xml_report)
|
26
|
-
|
27
|
-
puts "Saved #{rspec_report_file}"
|
28
|
-
|
29
|
-
json_rspec_report_file = rspec_report_file.gsub('.xml', '.json')
|
30
|
-
json_report = rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
|
31
|
-
|
32
|
-
File.write(json_rspec_report_file, json_report)
|
33
|
-
|
34
|
-
puts "Saved #{json_rspec_report_file}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
|
41
|
-
report = Nokogiri::XML(File.open(rspec_report_file))
|
42
|
-
|
43
|
-
report.xpath('//system-out').each do |system_out|
|
44
|
-
system_out.content = system_out.content.gsub(CONTAINER_PATH, artifact_path)
|
45
|
-
end
|
46
|
-
|
47
|
-
report.to_s
|
48
|
-
end
|
49
|
-
|
50
|
-
def rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
|
51
|
-
report = JSON.parse(File.read(json_rspec_report_file))
|
52
|
-
examples = report['examples']
|
53
|
-
|
54
|
-
examples.each do |example|
|
55
|
-
example['screenshot']['image'] = example['screenshot']['image'].gsub(CONTAINER_PATH, artifact_path) if example['screenshot'].present?
|
56
|
-
end
|
57
|
-
|
58
|
-
JSON.pretty_generate(report)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|