gitlab-qa 7.15.1 → 7.17.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eff5b2a5b6b59f95a7ce622eb57a6157dc7492af83be416d3b8950ba527c3495
4
- data.tar.gz: 6da73f8680896f2875e4899ea21b3272c418ee0140ae9d1a54b5117e870f5e5a
3
+ metadata.gz: ff50dfd062e99694daf1ff16ba5a2dc90009e115ae7135daefad599d10cf71d3
4
+ data.tar.gz: 65d3e0b16cfb76564db771428aa8ed8a308ef2cabb4573cefe39f418c81dfce2
5
5
  SHA512:
6
- metadata.gz: 6110c4e993d4ff262476a637e7fad86a32561c29609bd6a5341cf8d14a53de73a0c7301763a71a3674551cd36385d67ed42db86de2c434cd938c13cbb98a0afa
7
- data.tar.gz: 43db85b7549e3b3296df9f1d2c8534724d7c908ecaa11b300fe64f8a7b0f4297fbf88e704c8431fa2da4ce5a5247788136a6b66972cbbf7fd6dd1a6de3956dff
6
+ metadata.gz: 7270725434c4cdf1fa7b0630635087b8a9d4871929f705a97980b206a3a9cbe44c874974ae0fef215eb2521fa28c8f7bae866ec2e39ac13d16db95750bf82cb3
7
+ data.tar.gz: 7eb6bff95f128d1cf7a8ca99210cb8066a49871d4e676c22053e37e4137229a6d58e70c944267ee7a51ada4a803c3d00adbbc2c4b5c8083bbc3c79a068725795
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/quality/testcases"
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-in-issues "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_TESTCASES_REPORTING_PROJECT" || true; fi
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-in-issues "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_TESTCASES_REPORTING_PROJECT" || true; fi
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:
@@ -1332,7 +1333,7 @@ generate-allure-report:
1332
1333
  cache:
1333
1334
  policy: pull
1334
1335
  before_script:
1335
- - echo "Generating and publishing allure test report"
1336
+ - export ALLURE_JOB_NAME="${ALLURE_JOB_NAME:-package-and-qa}"
1336
1337
  rules:
1337
1338
  # Don't run report generation on release pipelines
1338
1339
  - if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab-qa" && $RELEASE == null'
@@ -1345,8 +1346,6 @@ generate-allure-report:
1345
1346
  ALLURE_JOB_NAME: gitlab-qa
1346
1347
  when: always
1347
1348
  - if: '$TOP_UPSTREAM_SOURCE_PROJECT && $TOP_UPSTREAM_SOURCE_SHA'
1348
- variables:
1349
- ALLURE_JOB_NAME: package-and-qa
1350
1349
  when: always
1351
1350
 
1352
1351
  include:
@@ -703,6 +703,9 @@ in the GitLab project).
703
703
 
704
704
  **Required environment variables:**
705
705
 
706
+ - `GITLAB_QA_USER_AGENT`: The browser user-agent to use instead of the default Chrome user-agent.
707
+ This is needed for the automated tests to bypass the WAF
708
+
706
709
  - `GITLAB_QA_ACCESS_TOKEN`: A valid personal access token with the `api` scope.
707
710
  This is used to retrieve the version that staging is currently running.
708
711
  This can be found in the shared 1Password vault.
@@ -718,6 +721,7 @@ in the GitLab project).
718
721
  Example:
719
722
 
720
723
  ```
724
+ $ export GITLAB_QA_USER_AGENT="$GITLAB_QA_USER_AGENT"
721
725
  $ export GITLAB_QA_ACCESS_TOKEN=your_api_access_token
722
726
  $ export GITLAB_QA_DEV_ACCESS_TOKEN=your_dev_registry_access_token
723
727
  $ export GITLAB_USERNAME="gitlab-qa"
data/gitlab-qa.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'solargraph', '~> 0.41'
29
29
  spec.add_development_dependency 'webmock', '3.7.0'
30
30
  spec.add_runtime_dependency 'activesupport', '~> 6.1'
31
- spec.add_runtime_dependency 'gitlab', '~> 4.16.1'
31
+ spec.add_runtime_dependency 'gitlab', '~> 4.18.0'
32
32
  spec.add_runtime_dependency 'http', '~> 5.0'
33
33
  spec.add_runtime_dependency 'nokogiri', '~> 1.10'
34
34
  spec.add_runtime_dependency 'table_print', '1.5.7'
@@ -8,7 +8,7 @@ module Gitlab
8
8
  # the `qa/` directory located in GitLab CE / EE repositories.
9
9
  #
10
10
  class Specs < Scenario::Template
11
- attr_accessor :suite, :release, :network, :args, :volumes, :env, :runner_network
11
+ attr_accessor :suite, :release, :network, :args, :volumes, :env, :runner_network, :hostname
12
12
 
13
13
  def initialize
14
14
  @docker = Docker::Engine.new
@@ -45,6 +45,11 @@ module Gitlab
45
45
  @docker.run(image: qa_image, args: [suite, *args_with_flags(args, feature_flag_set)]) do |command|
46
46
  command << "-t --rm --net=#{network || 'bridge'}"
47
47
 
48
+ unless hostname.nil?
49
+ command << "--hostname #{hostname}"
50
+ command.env('QA_HOSTNAME', hostname)
51
+ end
52
+
48
53
  env.merge(Runtime::Env.variables).each do |key, value|
49
54
  command.env(key, value)
50
55
  end
@@ -6,6 +6,11 @@ module Gitlab
6
6
  module QA
7
7
  module Report
8
8
  class GenerateTestSession < ReportAsIssue
9
+ def initialize(**kwargs)
10
+ super
11
+ @issue_type = 'issue'
12
+ end
13
+
9
14
  private
10
15
 
11
16
  # rubocop:disable Metrics/AbcSize
@@ -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 " => Created new issue: #{issue.web_url} for test '#{test.name}'." if issue
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,60 @@
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:, results_issue_project:, 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
+ def run!
26
+ puts "Reporting test results in `#{files.join(',')}` as test cases in project `#{test_case_project}`"\
27
+ " and issues in project `#{results_issue_project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
28
+
29
+ test_results_per_file do |test_results|
30
+ puts "Reporting tests in #{test_results.path}"
31
+
32
+ test_results.each do |test|
33
+ puts "Reporting test: #{test.file} | #{test.name}\n"
34
+
35
+ report_test(test) unless test.skipped
36
+ end
37
+
38
+ test_results.write
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def report_test(test)
45
+ testcase = testcase_project_reporter.find_or_create_testcase(test)
46
+ # The API returns the test case with an issue URL since it is technically a type of issue.
47
+ # This updates the URL to a valid test case link.
48
+ test.testcase = testcase.web_url.sub('/issues/', '/quality/test_cases/')
49
+
50
+ issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
51
+
52
+ testcase_project_reporter.add_issue_link_to_testcase(testcase, issue, test) if is_new
53
+
54
+ testcase_project_reporter.update_testcase(testcase, test)
55
+ results_issue_project_reporter.update_issue(issue, test)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ 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
- private
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
- test_results.each do |test|
22
- puts "Reporting test: #{test.file} | #{test.name}\n"
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 report_test(test)
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, 'issue') if issue_title_needs_updating?(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
- update_labels(testcase, test)
47
- update_issue(issue, test)
27
+ [issue, is_new]
48
28
  end
49
29
 
50
- def find_testcase(test)
51
- testcase = find_testcase_by_iid(test)
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 testcase
54
- testcase = update_issue_title(testcase, test, 'test_case') if issue_title_needs_updating?(testcase, test)
37
+ if labels_updated || note_posted
38
+ puts "Issue updated."
55
39
  else
56
- testcase = find_issue(test, 'test_case')
40
+ puts "Test passed, no results issue update needed."
57
41
  end
58
-
59
- testcase
60
42
  end
61
43
 
62
- def find_testcase_by_iid(test)
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, 'issue')
51
+ find_issue_by_iid(iid)
80
52
  end
81
53
 
82
- def find_issue_by_iid(iid, issue_type)
83
- issues = gitlab.find_issues(iid: iid) do |issue|
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(RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(RESULTS_SECTION_TEMPLATE)
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
@@ -17,8 +17,8 @@ module Gitlab
17
17
  report_options[:input_files] = files if files
18
18
  end
19
19
 
20
- opts.on('--report-in-issues FILES', String, 'Report test results from JUnit XML files in GitLab issues') do |files|
21
- report_options[:report_in_issues] = true
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 --report-in-issues and --relate-failure-issue') do |value|
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-in-issues and --relate-failure-issue') do |value|
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(:report_in_issues)
97
+ elsif report_options.delete(:report_results)
90
98
  report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
91
- Gitlab::QA::Report::ResultsInIssues.new(**report_options).invoke!
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])
@@ -19,6 +19,7 @@ module Gitlab
19
19
  gitlab.instance do
20
20
  Component::Specs.perform do |specs|
21
21
  specs.suite = 'Test::Instance::All'
22
+ specs.hostname = "qa-e2e-specs.#{gitlab.network}"
22
23
  specs.release = gitlab.release
23
24
  specs.network = gitlab.network
24
25
  specs.args = [gitlab.address, *rspec_args]
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gitlab
2
4
  module QA
3
- VERSION = '7.15.1'.freeze
5
+ VERSION = '7.17.1'
4
6
  end
5
7
  end
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.15.1
4
+ version: 7.17.1
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-02 00:00:00.000000000 Z
11
+ date: 2021-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 4.16.1
131
+ version: 4.18.0
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 4.16.1
138
+ version: 4.18.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: http
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -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