gitlab-qa 10.4.1 → 11.1.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 +4 -4
- data/.gitlab/changelog_config.yml +13 -0
- data/.gitlab/merge_request_templates/Release.md +9 -36
- data/.rubocop_todo.yml +0 -12
- data/Dangerfile +1 -5
- data/Gemfile.lock +4 -4
- data/README.md +1 -2
- data/docs/running_against_remote_grid.md +39 -2
- data/gitlab-qa.gemspec +1 -1
- data/lib/gitlab/qa/component/gitaly_cluster.rb +0 -1
- data/lib/gitlab/qa/component/gitlab.rb +10 -9
- data/lib/gitlab/qa/component/selenoid.rb +8 -3
- data/lib/gitlab/qa/runtime/env.rb +21 -41
- data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/mtls.rb +0 -1
- data/lib/gitlab/qa/scenario/test/integration/praefect.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.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 +4 -1
- metadata +5 -39
- 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/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,176 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
8
|
-
class ReportAsIssue
|
9
|
-
MAX_TITLE_LENGTH = 255
|
10
|
-
|
11
|
-
def initialize(token:, input_files:, project: nil, dry_run: false, **kwargs)
|
12
|
-
@project = project
|
13
|
-
@gitlab = (dry_run ? GitlabIssueDryClient : GitlabIssueClient).new(token: token, project: project)
|
14
|
-
@files = Array(input_files)
|
15
|
-
end
|
16
|
-
|
17
|
-
def invoke!
|
18
|
-
validate_input!
|
19
|
-
|
20
|
-
run!
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
attr_reader :gitlab, :files, :project, :issue_type
|
26
|
-
|
27
|
-
def run!
|
28
|
-
raise NotImplementedError
|
29
|
-
end
|
30
|
-
|
31
|
-
def new_issue_title(test)
|
32
|
-
"#{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
|
33
|
-
end
|
34
|
-
|
35
|
-
def new_issue_description(test)
|
36
|
-
"### Full description\n\n#{search_safe(test.name)}\n\n### File path\n\n#{test.file}"
|
37
|
-
end
|
38
|
-
|
39
|
-
def new_issue_labels(test)
|
40
|
-
[]
|
41
|
-
end
|
42
|
-
|
43
|
-
def validate_input!
|
44
|
-
assert_project!
|
45
|
-
assert_input_files!(files)
|
46
|
-
gitlab.assert_user_permission!
|
47
|
-
end
|
48
|
-
|
49
|
-
def assert_project!
|
50
|
-
return if project
|
51
|
-
|
52
|
-
abort "Please provide a valid project ID or path with the `-p/--project` option!"
|
53
|
-
end
|
54
|
-
|
55
|
-
def assert_input_files!(files)
|
56
|
-
return if Dir.glob(files).any?
|
57
|
-
|
58
|
-
abort "Please provide valid JUnit report files. No files were found matching `#{files.join(',')}`"
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_results_per_file
|
62
|
-
Dir.glob(files).each do |path|
|
63
|
-
extension = File.extname(path)
|
64
|
-
|
65
|
-
test_results =
|
66
|
-
case extension
|
67
|
-
when '.json'
|
68
|
-
Report::JsonTestResults.new(path)
|
69
|
-
when '.xml'
|
70
|
-
Report::JUnitTestResults.new(path)
|
71
|
-
else
|
72
|
-
raise "Unknown extension #{extension}"
|
73
|
-
end
|
74
|
-
|
75
|
-
yield test_results
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def create_issue(test)
|
80
|
-
issue = gitlab.create_issue(
|
81
|
-
title: title_from_test(test),
|
82
|
-
description: new_issue_description(test),
|
83
|
-
labels: new_issue_labels(test).to_a,
|
84
|
-
issue_type: issue_type
|
85
|
-
)
|
86
|
-
|
87
|
-
new_link = issue_type == 'test_case' ? issue.web_url.sub('/issues/', '/quality/test_cases/') : issue.web_url
|
88
|
-
|
89
|
-
puts "Created new #{issue_type}: #{new_link}"
|
90
|
-
|
91
|
-
issue
|
92
|
-
end
|
93
|
-
|
94
|
-
def issue_labels(issue)
|
95
|
-
issue&.labels&.to_set || Set.new
|
96
|
-
end
|
97
|
-
|
98
|
-
def update_labels(issue, test, new_labels = Set.new)
|
99
|
-
labels = up_to_date_labels(test: test, issue: issue, new_labels: new_labels)
|
100
|
-
|
101
|
-
return if issue_labels(issue) == labels
|
102
|
-
|
103
|
-
gitlab.edit_issue(iid: issue.iid, options: { labels: labels.to_a })
|
104
|
-
end
|
105
|
-
|
106
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
107
|
-
labels = issue_labels(issue)
|
108
|
-
labels |= new_labels
|
109
|
-
ee_test?(test) ? labels << 'Enterprise Edition' : labels.delete('Enterprise Edition')
|
110
|
-
|
111
|
-
if test.quarantine?
|
112
|
-
labels << 'quarantine'
|
113
|
-
labels << "quarantine::#{test.quarantine_type}"
|
114
|
-
else
|
115
|
-
labels.delete_if { |label| label.include?('quarantine') }
|
116
|
-
end
|
117
|
-
|
118
|
-
labels
|
119
|
-
end
|
120
|
-
|
121
|
-
def pipeline_name_label
|
122
|
-
case pipeline
|
123
|
-
when 'production'
|
124
|
-
'found:gitlab.com'
|
125
|
-
when 'canary', 'staging'
|
126
|
-
"found:#{pipeline}.gitlab.com"
|
127
|
-
when 'staging-canary'
|
128
|
-
"found:canary.staging.gitlab.com"
|
129
|
-
when 'preprod'
|
130
|
-
'found:pre.gitlab.com'
|
131
|
-
when 'nightly', QA::Runtime::Env.default_branch, 'staging-ref', 'release'
|
132
|
-
"found:#{pipeline}"
|
133
|
-
else
|
134
|
-
raise "No `found:*` label for the `#{pipeline}` pipeline!"
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def ee_test?(test)
|
139
|
-
test.file =~ %r{features/ee/(api|browser_ui)}
|
140
|
-
end
|
141
|
-
|
142
|
-
def partial_file_path(path)
|
143
|
-
path.match(/((ee|api|browser_ui).*)/i)[1]
|
144
|
-
end
|
145
|
-
|
146
|
-
def title_from_test(test)
|
147
|
-
title = new_issue_title(test)
|
148
|
-
|
149
|
-
return title unless title.length > MAX_TITLE_LENGTH
|
150
|
-
|
151
|
-
"#{title[0...MAX_TITLE_LENGTH - 3]}..."
|
152
|
-
end
|
153
|
-
|
154
|
-
def search_safe(value)
|
155
|
-
value.delete('"')
|
156
|
-
end
|
157
|
-
|
158
|
-
def pipeline
|
159
|
-
# Gets the name of the pipeline the test was run in, to be used as the key of a scoped label
|
160
|
-
#
|
161
|
-
# Tests can be run in several pipelines:
|
162
|
-
# gitlab, nightly, staging, canary, production, preprod, MRs, and the default branch (master/main)
|
163
|
-
#
|
164
|
-
# Some of those run in their own project, so CI_PROJECT_NAME is the name we need. Those are:
|
165
|
-
# nightly, staging, canary, production, and preprod
|
166
|
-
#
|
167
|
-
# MR, master/main, and gitlab tests run in gitlab-qa, but we only want to report tests run on
|
168
|
-
# master/main because the other pipelines will be monitored by the author of the MR that triggered them.
|
169
|
-
# So we assume that we're reporting a master/main pipeline if the project name is 'gitlab'.
|
170
|
-
|
171
|
-
@pipeline ||= Runtime::Env.pipeline_from_project_name
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
@@ -1,64 +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 cases and issues with the results of tests from RSpec report files.
|
7
|
-
class ReportResults < ReportAsIssue
|
8
|
-
attr_accessor :testcase_project_reporter, :results_issue_project_reporter, :files, :test_case_project, :results_issue_project, :gitlab
|
9
|
-
|
10
|
-
def initialize(token:, input_files:, test_case_project: nil, results_issue_project: nil, dry_run: false, **kwargs)
|
11
|
-
@testcase_project_reporter = Gitlab::QA::Report::ResultsInTestCases.new(token: token, input_files: input_files, project: test_case_project, dry_run: dry_run, **kwargs)
|
12
|
-
@results_issue_project_reporter = Gitlab::QA::Report::ResultsInIssues.new(token: token, input_files: input_files, project: results_issue_project, dry_run: dry_run, **kwargs)
|
13
|
-
@test_case_project = test_case_project
|
14
|
-
@results_issue_project = results_issue_project
|
15
|
-
@files = Array(input_files)
|
16
|
-
@gitlab = testcase_project_reporter.gitlab
|
17
|
-
end
|
18
|
-
|
19
|
-
def assert_project!
|
20
|
-
return if test_case_project && results_issue_project
|
21
|
-
|
22
|
-
abort "Please provide valid project IDs or paths with the `--results-issue-project` and `--test-case-project` options!"
|
23
|
-
end
|
24
|
-
|
25
|
-
# rubocop:disable Metrics/AbcSize
|
26
|
-
def run!
|
27
|
-
puts "Reporting test results in `#{files.join(',')}` as test cases in project `#{test_case_project}`"\
|
28
|
-
" and issues in project `#{results_issue_project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
29
|
-
|
30
|
-
test_results_per_file do |test_results|
|
31
|
-
puts "Reporting tests in #{test_results.path}"
|
32
|
-
|
33
|
-
test_results.each do |test|
|
34
|
-
next if test.file.include?('/features/sanity/') || test.skipped
|
35
|
-
|
36
|
-
puts "Reporting test: #{test.file} | #{test.name}\n"
|
37
|
-
|
38
|
-
report_test(test)
|
39
|
-
end
|
40
|
-
|
41
|
-
test_results.write
|
42
|
-
end
|
43
|
-
end
|
44
|
-
# rubocop:enable Metrics/AbcSize
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def report_test(test)
|
49
|
-
testcase = testcase_project_reporter.find_or_create_testcase(test)
|
50
|
-
# The API returns the test case with an issue URL since it is technically a type of issue.
|
51
|
-
# This updates the URL to a valid test case link.
|
52
|
-
test.testcase = testcase.web_url.sub('/issues/', '/quality/test_cases/')
|
53
|
-
|
54
|
-
issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
|
55
|
-
|
56
|
-
testcase_project_reporter.add_issue_link_to_testcase(testcase, issue, test) if is_new
|
57
|
-
|
58
|
-
testcase_project_reporter.update_testcase(testcase, test)
|
59
|
-
results_issue_project_reporter.update_issue(issue, test)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -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
|