gitlab-qa 7.15.1 → 7.16.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|