gitlab-qa 7.15.1 → 7.16.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-ci.yml +4 -3
- data/lib/gitlab/qa/report/generate_test_session.rb +5 -0
- data/lib/gitlab/qa/report/relate_failure_issue.rb +2 -1
- data/lib/gitlab/qa/report/report_as_issue.rb +10 -3
- data/lib/gitlab/qa/report/report_results.rb +71 -0
- data/lib/gitlab/qa/report/results_in_issues.rb +24 -146
- data/lib/gitlab/qa/report/results_in_testcases.rb +67 -0
- data/lib/gitlab/qa/report/results_reporter_shared.rb +65 -0
- data/lib/gitlab/qa/reporter.rb +15 -7
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '090fb8d7830e6cfd3ee6978b9f7ba4ad6eb4ba5ceec37921e5cde75285fd4015'
|
4
|
+
data.tar.gz: '058a34d1526fc7ae37df2c8b75e3e0888c15785c5bb2d3954b0b72e6a6d653d8'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de89ce6d1c4a5cf43aebd3510af3f758dc1e8f2d8063878ec6a74d5530d9af9ffa903ad77267caed55ad77125ecb2aa044523cb9366144c82637f02bb765778f
|
7
|
+
data.tar.gz: 9a68591bf7a1a774dbee63a5f0374c0b46df7029e4e90419f184be961cfbddb7d31589e7603299bb1b7f59af20ceb7f2eb5f4071315389dc97958946bc246801
|
data/.gitlab-ci.yml
CHANGED
@@ -46,7 +46,8 @@ variables:
|
|
46
46
|
QA_CAN_TEST_GIT_PROTOCOL_V2: "true"
|
47
47
|
QA_CAN_TEST_PRAEFECT: "false"
|
48
48
|
QA_GENERATE_ALLURE_REPORT: "true"
|
49
|
-
QA_TESTCASES_REPORTING_PROJECT: "gitlab-org/
|
49
|
+
QA_TESTCASES_REPORTING_PROJECT: "gitlab-org/gitlab"
|
50
|
+
QA_TEST_RESULTS_ISSUES_PROJECT: "gitlab-org/quality/testcases"
|
50
51
|
QA_TESTCASE_SESSIONS_PROJECT: "gitlab-org/quality/testcase-sessions"
|
51
52
|
# QA_DEFAULT_BRANCH is the default branch name of the instance under test.
|
52
53
|
QA_DEFAULT_BRANCH: "master"
|
@@ -84,7 +85,7 @@ rspec:
|
|
84
85
|
- bundle exec exe/gitlab-qa ${QA_SCENARIO:=Test::Instance::Image} ${RELEASE:=$DEFAULT_RELEASE} $GITLAB_QA_OPTIONS -- $QA_TESTS $QA_RSPEC_TAGS $RSPEC_REPORT_OPTS || test_run_exit_code=$?
|
85
86
|
- bundle exec exe/gitlab-qa-report --update-screenshot-path "gitlab-qa-run-*/**/rspec-*.xml"
|
86
87
|
- export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
|
87
|
-
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-
|
88
|
+
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-results "gitlab-qa-run-*/**/rspec-*.json" --test-case-project "$QA_TESTCASES_REPORTING_PROJECT" --results-issue-project "$QA_TEST_RESULTS_ISSUES_PROJECT" || true; fi
|
88
89
|
- exit $test_run_exit_code
|
89
90
|
|
90
91
|
# For jobs that shouldn't report results in issues, e.g., manually run custom jobs
|
@@ -107,7 +108,7 @@ rspec:
|
|
107
108
|
- bundle exec exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=$DEFAULT_RELEASE} ${RELEASE:=$DEFAULT_RELEASE} $GITLAB_QA_OPTIONS -- $QA_TESTS $QA_RSPEC_TAGS $RSPEC_REPORT_OPTS || test_run_exit_code=$?
|
108
109
|
- bundle exec exe/gitlab-qa-report --update-screenshot-path "gitlab-qa-run-*/**/rspec-*.xml"
|
109
110
|
- export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
|
110
|
-
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-
|
111
|
+
- if [ "$TOP_UPSTREAM_SOURCE_REF" == $TOP_UPSTREAM_DEFAULT_BRANCH ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --report-results "gitlab-qa-run-*/**/rspec-*.json" --test-case-project "$QA_TESTCASES_REPORTING_PROJECT" --results-issue-project "$QA_TEST_RESULTS_ISSUES_PROJECT" || true; fi
|
111
112
|
- exit $test_run_exit_code
|
112
113
|
|
113
114
|
.ce-variables:
|
@@ -19,6 +19,7 @@ module Gitlab
|
|
19
19
|
def initialize(max_diff_ratio: DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION, **kwargs)
|
20
20
|
super
|
21
21
|
@max_diff_ratio = max_diff_ratio.to_f
|
22
|
+
@issue_type = 'issue'
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
@@ -63,7 +64,7 @@ module Gitlab
|
|
63
64
|
puts " => Found issue #{issue.web_url} for test '#{test.name}' with a diff ratio of #{(diff_ratio * 100).round(2)}%."
|
64
65
|
else
|
65
66
|
issue = create_issue(test)
|
66
|
-
puts "
|
67
|
+
puts "for test '#{test.name}'."
|
67
68
|
end
|
68
69
|
|
69
70
|
issue
|
@@ -22,7 +22,7 @@ module Gitlab
|
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
-
attr_reader :gitlab, :files, :project
|
25
|
+
attr_reader :gitlab, :files, :project, :issue_type
|
26
26
|
|
27
27
|
def run!
|
28
28
|
raise NotImplementedError
|
@@ -77,11 +77,18 @@ module Gitlab
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def create_issue(test)
|
80
|
-
gitlab.create_issue(
|
80
|
+
issue = gitlab.create_issue(
|
81
81
|
title: title_from_test(test),
|
82
82
|
description: new_issue_description(test),
|
83
|
-
labels: new_issue_labels(test).to_a
|
83
|
+
labels: new_issue_labels(test).to_a,
|
84
|
+
issue_type: issue_type
|
84
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
|
85
92
|
end
|
86
93
|
|
87
94
|
def issue_labels(issue)
|
@@ -0,0 +1,71 @@
|
|
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
|
+
# TODO: Remove old_testcase_project_reporter once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
|
9
|
+
attr_accessor :testcase_project_reporter, :results_issue_project_reporter, :files, :test_case_project, :results_issue_project, :gitlab, :old_testcase_project_reporter
|
10
|
+
|
11
|
+
def initialize(token:, input_files:, test_case_project:, results_issue_project:, dry_run: false, **kwargs)
|
12
|
+
@testcase_project_reporter = Gitlab::QA::Report::ResultsInTestCases.new(token: token, input_files: input_files, project: test_case_project, dry_run: dry_run, **kwargs)
|
13
|
+
# TODO: Remove the line below once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
|
14
|
+
@old_testcase_project_reporter = Gitlab::QA::Report::ResultsInTestCases.new(token: token, input_files: input_files, project: results_issue_project, dry_run: dry_run, **kwargs)
|
15
|
+
@results_issue_project_reporter = Gitlab::QA::Report::ResultsInIssues.new(token: token, input_files: input_files, project: results_issue_project, dry_run: dry_run, **kwargs)
|
16
|
+
@test_case_project = test_case_project
|
17
|
+
@results_issue_project = results_issue_project
|
18
|
+
@files = Array(input_files)
|
19
|
+
@gitlab = testcase_project_reporter.gitlab
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_project!
|
23
|
+
return if test_case_project && results_issue_project
|
24
|
+
|
25
|
+
abort "Please provide valid project IDs or paths with the `--results-issue-project` and `--test-case-project` options!"
|
26
|
+
end
|
27
|
+
|
28
|
+
def run!
|
29
|
+
puts "Reporting test results in `#{files.join(',')}` as test cases in project `#{test_case_project}`"\
|
30
|
+
" and issues in project `#{results_issue_project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
31
|
+
|
32
|
+
test_results_per_file do |test_results|
|
33
|
+
puts "Reporting tests in #{test_results.path}"
|
34
|
+
|
35
|
+
test_results.each do |test|
|
36
|
+
puts "Reporting test: #{test.file} | #{test.name}\n"
|
37
|
+
|
38
|
+
report_test(test) unless test.skipped
|
39
|
+
end
|
40
|
+
|
41
|
+
test_results.write
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def report_test(test)
|
48
|
+
# TODO: Remove the line below and replace correct_testcase_project_reporter with testcase_project_reporter
|
49
|
+
# once all test case links are updated to the gitlab project. See https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1079
|
50
|
+
correct_testcase_project_reporter = using_old_testcase_link(test) ? old_testcase_project_reporter : testcase_project_reporter
|
51
|
+
|
52
|
+
testcase = correct_testcase_project_reporter.find_or_create_testcase(test)
|
53
|
+
# The API returns the test case with an issue URL since it is technically a type of issue.
|
54
|
+
# This updates the URL to a valid test case link.
|
55
|
+
test.testcase = testcase.web_url.sub('/issues/', '/quality/test_cases/')
|
56
|
+
|
57
|
+
issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
|
58
|
+
|
59
|
+
correct_testcase_project_reporter.add_issue_link_to_testcase(testcase, issue, test) if is_new
|
60
|
+
|
61
|
+
correct_testcase_project_reporter.update_testcase(testcase, test)
|
62
|
+
results_issue_project_reporter.update_issue(issue, test)
|
63
|
+
end
|
64
|
+
|
65
|
+
def using_old_testcase_link(test)
|
66
|
+
test.testcase.include?(results_issue_project)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,124 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'active_support/core_ext/enumerable'
|
5
|
-
|
6
3
|
module Gitlab
|
7
4
|
module QA
|
8
5
|
module Report
|
9
|
-
# Uses the API to create or update GitLab issues with the results of tests from RSpec report files.
|
6
|
+
# Uses the API to create or update GitLab test result issues with the results of tests from RSpec report files.
|
10
7
|
class ResultsInIssues < ReportAsIssue
|
11
|
-
|
12
|
-
|
13
|
-
RESULTS_SECTION_TEMPLATE = "\n\n### DO NOT EDIT BELOW THIS LINE\n\nActive and historical test results:"
|
14
|
-
|
15
|
-
def run!
|
16
|
-
puts "Reporting test results in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
17
|
-
|
18
|
-
test_results_per_file do |test_results|
|
19
|
-
puts "Reporting tests in #{test_results.path}"
|
8
|
+
include ResultsReporterShared
|
20
9
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
report_test(test) unless test.skipped
|
25
|
-
end
|
26
|
-
|
27
|
-
test_results.write
|
28
|
-
end
|
10
|
+
def initialize(**kwargs)
|
11
|
+
super
|
12
|
+
@issue_type = 'issue'
|
29
13
|
end
|
30
14
|
|
31
|
-
def
|
32
|
-
testcase = find_testcase(test) || create_testcase(test)
|
33
|
-
test.testcase ||= testcase.web_url.sub('/issues/', '/quality/test_cases/')
|
34
|
-
|
15
|
+
def get_related_issue(testcase, test)
|
35
16
|
issue = find_linked_results_issue_by_iid(testcase, test)
|
17
|
+
is_new = false
|
36
18
|
|
37
19
|
if issue
|
38
|
-
issue = update_issue_title(issue, test
|
20
|
+
issue = update_issue_title(issue, test) if issue_title_needs_updating?(issue, test)
|
39
21
|
else
|
40
22
|
puts "No valid issue link found"
|
41
23
|
issue = find_or_create_results_issue(test)
|
42
|
-
|
43
|
-
add_issue_to_testcase(testcase, issue)
|
24
|
+
is_new = true
|
44
25
|
end
|
45
26
|
|
46
|
-
|
47
|
-
update_issue(issue, test)
|
27
|
+
[issue, is_new]
|
48
28
|
end
|
49
29
|
|
50
|
-
def
|
51
|
-
|
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)
|
52
36
|
|
53
|
-
if
|
54
|
-
|
37
|
+
if labels_updated || note_posted
|
38
|
+
puts "Issue updated."
|
55
39
|
else
|
56
|
-
|
40
|
+
puts "Test passed, no results issue update needed."
|
57
41
|
end
|
58
|
-
|
59
|
-
testcase
|
60
42
|
end
|
61
43
|
|
62
|
-
|
63
|
-
iid = testcase_iid_from_url(test.testcase)
|
64
|
-
|
65
|
-
return unless iid
|
66
|
-
|
67
|
-
find_issue_by_iid(iid, 'test_case')
|
68
|
-
end
|
69
|
-
|
70
|
-
def issue_title_needs_updating?(issue, test)
|
71
|
-
issue.title.strip != title_from_test(test) && !%w[canary production preprod release].include?(pipeline)
|
72
|
-
end
|
44
|
+
private
|
73
45
|
|
74
46
|
def find_linked_results_issue_by_iid(testcase, test)
|
75
47
|
iid = issue_iid_from_testcase(testcase)
|
76
48
|
|
77
49
|
return unless iid
|
78
50
|
|
79
|
-
find_issue_by_iid(iid
|
51
|
+
find_issue_by_iid(iid)
|
80
52
|
end
|
81
53
|
|
82
|
-
def
|
83
|
-
|
84
|
-
issue.state == 'opened' && issue.issue_type == issue_type
|
85
|
-
end
|
86
|
-
|
87
|
-
warn(%(#{issue_type} iid "#{iid}" not valid)) if issues.empty?
|
88
|
-
|
89
|
-
issues.first
|
90
|
-
end
|
91
|
-
|
92
|
-
def update_issue_title(issue, test, issue_type)
|
93
|
-
warn(%(#{issue_type} title needs to be updated from '#{issue.title.strip}' to '#{title_from_test(test)}'))
|
94
|
-
|
95
|
-
gitlab.edit_issue(iid: issue.iid, options: { title: title_from_test(test) })
|
96
|
-
end
|
97
|
-
|
98
|
-
def create_testcase(test)
|
99
|
-
title = title_from_test(test)
|
100
|
-
puts "Creating test case '#{title}' ..."
|
101
|
-
|
102
|
-
gitlab.create_issue(
|
103
|
-
title: title,
|
104
|
-
description: new_testcase_description(test),
|
105
|
-
labels: new_issue_labels(test),
|
106
|
-
issue_type: 'test_case'
|
107
|
-
)
|
108
|
-
end
|
109
|
-
|
110
|
-
def testcase_iid_from_url(url)
|
111
|
-
return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
|
112
|
-
|
113
|
-
url && url.split('/').last.to_i
|
114
|
-
end
|
115
|
-
|
116
|
-
def new_testcase_description(test)
|
117
|
-
"#{new_issue_description(test)}#{RESULTS_SECTION_TEMPLATE}"
|
54
|
+
def find_or_create_results_issue(test)
|
55
|
+
find_issue(test) || create_issue(test)
|
118
56
|
end
|
119
57
|
|
120
58
|
def issue_iid_from_testcase(testcase)
|
121
|
-
results = testcase.description.partition(
|
59
|
+
results = testcase.description.partition(TEST_CASE_RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE)
|
122
60
|
|
123
61
|
return puts "No issue link found" unless results
|
124
62
|
|
@@ -127,66 +65,6 @@ module Gitlab
|
|
127
65
|
issue_iid&.to_i
|
128
66
|
end
|
129
67
|
|
130
|
-
def find_or_create_results_issue(test)
|
131
|
-
issue = find_issue(test, 'issue')
|
132
|
-
|
133
|
-
if issue
|
134
|
-
puts "Found existing issue: #{issue.web_url}"
|
135
|
-
else
|
136
|
-
issue = create_issue(test)
|
137
|
-
puts "Created new issue: #{issue.web_url}"
|
138
|
-
end
|
139
|
-
|
140
|
-
issue
|
141
|
-
end
|
142
|
-
|
143
|
-
def find_issue(test, issue_type)
|
144
|
-
issues = gitlab.find_issues(options: { search: search_term(test) }) do |issue|
|
145
|
-
issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
|
146
|
-
end
|
147
|
-
|
148
|
-
warn(%(Too many #{issue_type}s found with the file path "#{test.file}" and name "#{test.name}")) if issues.many?
|
149
|
-
|
150
|
-
issues.first
|
151
|
-
end
|
152
|
-
|
153
|
-
def add_issue_to_testcase(testcase, issue)
|
154
|
-
results_section = testcase.description.include?(RESULTS_SECTION_TEMPLATE) ? '' : RESULTS_SECTION_TEMPLATE
|
155
|
-
|
156
|
-
gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
|
157
|
-
|
158
|
-
puts "Added issue #{issue.web_url} to testcase #{testcase.web_url}"
|
159
|
-
end
|
160
|
-
|
161
|
-
def update_issue(issue, test)
|
162
|
-
new_labels = issue_labels(issue)
|
163
|
-
new_labels |= ['Testcase Linked']
|
164
|
-
|
165
|
-
labels_updated = update_labels(issue, test, new_labels)
|
166
|
-
note_posted = note_status(issue, test)
|
167
|
-
|
168
|
-
if labels_updated || note_posted
|
169
|
-
puts "Issue updated."
|
170
|
-
else
|
171
|
-
puts "Test passed, no update needed."
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def new_issue_labels(test)
|
176
|
-
['Quality', "devops::#{test.stage}", 'status::automated']
|
177
|
-
end
|
178
|
-
|
179
|
-
def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
|
180
|
-
labels = super
|
181
|
-
labels |= new_issue_labels(test).to_set
|
182
|
-
labels.delete_if { |label| label.start_with?("#{pipeline}::") }
|
183
|
-
labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
|
184
|
-
end
|
185
|
-
|
186
|
-
def search_term(test)
|
187
|
-
%("#{partial_file_path(test.file)}" "#{search_safe(test.name)}")
|
188
|
-
end
|
189
|
-
|
190
68
|
def note_status(issue, test)
|
191
69
|
return false if test.skipped
|
192
70
|
return false if test.failures.empty?
|
@@ -0,0 +1,67 @@
|
|
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 with the results of tests from RSpec report files.
|
7
|
+
class ResultsInTestCases < ReportAsIssue
|
8
|
+
attr_reader :issue_type, :gitlab
|
9
|
+
|
10
|
+
include ResultsReporterShared
|
11
|
+
|
12
|
+
def initialize(**kwargs)
|
13
|
+
super
|
14
|
+
@issue_type = 'test_case'
|
15
|
+
end
|
16
|
+
|
17
|
+
def find_or_create_testcase(test)
|
18
|
+
find_testcase(test) || create_issue(test)
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_issue_link_to_testcase(testcase, issue, test)
|
22
|
+
results_section = testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE) ? '' : TEST_CASE_RESULTS_SECTION_TEMPLATE
|
23
|
+
|
24
|
+
gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
|
25
|
+
# We are using test.testcase for the url here instead of testcase.web_url since it has the updated test case path
|
26
|
+
puts "Added results issue #{issue.web_url} link to test case #{test.testcase}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_testcase(testcase, test)
|
30
|
+
puts "Test case labels updated." if update_labels(testcase, test)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def find_testcase(test)
|
36
|
+
testcase = find_testcase_by_iid(test)
|
37
|
+
|
38
|
+
if testcase
|
39
|
+
testcase = update_issue_title(testcase, test) if issue_title_needs_updating?(testcase, test)
|
40
|
+
else
|
41
|
+
testcase = find_issue(test)
|
42
|
+
end
|
43
|
+
|
44
|
+
testcase
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_testcase_by_iid(test)
|
48
|
+
iid = testcase_iid_from_url(test.testcase)
|
49
|
+
|
50
|
+
return unless iid
|
51
|
+
|
52
|
+
find_issue_by_iid(iid)
|
53
|
+
end
|
54
|
+
|
55
|
+
def testcase_iid_from_url(url)
|
56
|
+
return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
|
57
|
+
|
58
|
+
url && url.split('/').last.to_i
|
59
|
+
end
|
60
|
+
|
61
|
+
def new_issue_description(test)
|
62
|
+
"#{super}#{TEST_CASE_RESULTS_SECTION_TEMPLATE}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,65 @@
|
|
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
|
+
['Quality', "devops::#{test.stage}", '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
|
+
warn(%(#{issue_type} title needs to be updated from '#{issue.title.strip}' to '#{title_from_test(test)}'))
|
51
|
+
|
52
|
+
gitlab.edit_issue(iid: issue.iid, options: { title: title_from_test(test) })
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def search_for_issues(test)
|
58
|
+
gitlab.find_issues(options: { search: search_term(test) }) do |issue|
|
59
|
+
issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/gitlab/qa/reporter.rb
CHANGED
@@ -17,8 +17,8 @@ module Gitlab
|
|
17
17
|
report_options[:input_files] = files if files
|
18
18
|
end
|
19
19
|
|
20
|
-
opts.on('--report-
|
21
|
-
report_options[:
|
20
|
+
opts.on('--report-results FILES', String, 'Report test results from JUnit XML files in GitLab test cases and results issues') do |files|
|
21
|
+
report_options[:report_results] = true
|
22
22
|
report_options[:input_files] = files if files
|
23
23
|
end
|
24
24
|
|
@@ -31,16 +31,24 @@ module Gitlab
|
|
31
31
|
report_options[:max_diff_ratio] = value
|
32
32
|
end
|
33
33
|
|
34
|
-
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --
|
34
|
+
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --relate-failure-issue') do |value|
|
35
35
|
report_options[:project] = value
|
36
36
|
end
|
37
37
|
|
38
|
+
opts.on('--results-issue-project RESULTS_ISSUE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
|
39
|
+
report_options[:results_issue_project] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--test-case-project TEST_CASE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
|
43
|
+
report_options[:test_case_project] = value
|
44
|
+
end
|
45
|
+
|
38
46
|
opts.on('--generate-test-session FILES', String, 'Generate test session report') do |files|
|
39
47
|
report_options[:generate_test_session] = true
|
40
48
|
report_options[:input_files] = files if files
|
41
49
|
end
|
42
50
|
|
43
|
-
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-
|
51
|
+
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-results and --relate-failure-issue') do |value|
|
44
52
|
report_options[:token] = value
|
45
53
|
end
|
46
54
|
|
@@ -60,7 +68,7 @@ module Gitlab
|
|
60
68
|
report_options[:files] = files
|
61
69
|
end
|
62
70
|
|
63
|
-
opts.on('--dry-run', "Perform a dry-run (don't create or update issues)") do |files|
|
71
|
+
opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do |files|
|
64
72
|
report_options[:dry_run] = true
|
65
73
|
end
|
66
74
|
|
@@ -86,9 +94,9 @@ module Gitlab
|
|
86
94
|
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
87
95
|
Gitlab::QA::Report::RelateFailureIssue.new(**report_options).invoke!
|
88
96
|
|
89
|
-
elsif report_options.delete(:
|
97
|
+
elsif report_options.delete(:report_results)
|
90
98
|
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
91
|
-
Gitlab::QA::Report::
|
99
|
+
Gitlab::QA::Report::ReportResults.new(**report_options).invoke!
|
92
100
|
|
93
101
|
elsif report_options.delete(:generate_test_session)
|
94
102
|
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
data/lib/gitlab/qa/version.rb
CHANGED
data/lib/gitlab/qa.rb
CHANGED
@@ -123,7 +123,10 @@ module Gitlab
|
|
123
123
|
autoload :JUnitTestResults, 'gitlab/qa/report/junit_test_results'
|
124
124
|
autoload :PrepareStageReports, 'gitlab/qa/report/prepare_stage_reports'
|
125
125
|
autoload :ReportAsIssue, 'gitlab/qa/report/report_as_issue'
|
126
|
+
autoload :ReportResults, 'gitlab/qa/report/report_results'
|
126
127
|
autoload :ResultsInIssues, 'gitlab/qa/report/results_in_issues'
|
128
|
+
autoload :ResultsInTestCases, 'gitlab/qa/report/results_in_testcases'
|
129
|
+
autoload :ResultsReporterShared, 'gitlab/qa/report/results_reporter_shared'
|
127
130
|
autoload :GenerateTestSession, 'gitlab/qa/report/generate_test_session'
|
128
131
|
autoload :SummaryTable, 'gitlab/qa/report/summary_table'
|
129
132
|
autoload :TestResult, 'gitlab/qa/report/test_result'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-qa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.16.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: 2021-12-
|
11
|
+
date: 2021-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -256,7 +256,10 @@ files:
|
|
256
256
|
- lib/gitlab/qa/report/prepare_stage_reports.rb
|
257
257
|
- lib/gitlab/qa/report/relate_failure_issue.rb
|
258
258
|
- lib/gitlab/qa/report/report_as_issue.rb
|
259
|
+
- lib/gitlab/qa/report/report_results.rb
|
259
260
|
- lib/gitlab/qa/report/results_in_issues.rb
|
261
|
+
- lib/gitlab/qa/report/results_in_testcases.rb
|
262
|
+
- lib/gitlab/qa/report/results_reporter_shared.rb
|
260
263
|
- lib/gitlab/qa/report/summary_table.rb
|
261
264
|
- lib/gitlab/qa/report/test_result.rb
|
262
265
|
- lib/gitlab/qa/report/update_screenshot_path.rb
|