gitlab-qa 6.4.0 → 6.8.0

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: 12e0b622362bae1054406a72f79f42a29582f9ebca8162a2c3f2d8128c9938f6
4
- data.tar.gz: fbaa6130f08f1a082cb90e4e1a5c65d7fb253b9f311cbaeb5937b3aa752ce20b
3
+ metadata.gz: daf050b7d871f8eefdc1fa68b052608c11ee186b927fe89accfceca15291ed45
4
+ data.tar.gz: 4510e98db8e41c11a1585070a39cd19769384917cff551195942955974dcd78d
5
5
  SHA512:
6
- metadata.gz: 9bdd569f223141416086e574911e71fc2350b55b487644ccdc68524cee0da8ee0c5ddaa9c26be1877cc2b218045b736a3212b71a26188f3770ac8355bfd8cf69
7
- data.tar.gz: e4a83ae9c48728baef2ea881f1abcd50476ce96e59ef1579b788eb6c58812c9aa27a40397caaf7293c71dd5bf1e6614e5607d2d41d438447c3badeafc9cd4595
6
+ metadata.gz: 3a3a6dc22aac1a77fe46f1379a43b489d08cc771b38ea629f9ff3a8f24fd9886f767ed3b3ca8aa18bfeabadc9019aefe2933261c969167e6c8e95f7f75e2c6a1
7
+ data.tar.gz: c612e3b42691a28864bcf703fdf89cbbf57f75817341f867d1e8486a25adcbfcf262f3506157841d734813e699e628620dd524af22b4f8da17c689a96c6116cf
@@ -2,10 +2,11 @@ stages:
2
2
  - check
3
3
  - release
4
4
  - test
5
+ - report
5
6
  - notify
6
7
 
7
8
  default:
8
- image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-ruby-2.6
9
+ image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-ruby-2.7
9
10
  tags:
10
11
  - gitlab-org
11
12
  cache:
@@ -48,6 +49,10 @@ variables:
48
49
  QA_CAN_TEST_GIT_PROTOCOL_V2: "true"
49
50
  QA_CAN_TEST_PRAEFECT: "false"
50
51
  QA_TESTCASES_REPORTING_PROJECT: "gitlab-org/quality/testcases"
52
+ QA_FAILURES_REPORTING_PROJECT: "gitlab-org/gitlab"
53
+ # The --dry-run or --max-diff-ratio option can be set to modify the behavior of `exe/gitlab-qa-report --relate-failure-issue` without releasing a new gem version.
54
+ QA_FAILURES_REPORTER_OPTIONS: "--dry-run"
55
+ QA_TESTCASE_SESSIONS_PROJECT: "gitlab-org/quality/testcase-sessions"
51
56
 
52
57
  .check-base:
53
58
  stage: check
@@ -95,6 +100,7 @@ release:
95
100
  - exe/gitlab-qa-report --update-screenshot-path "gitlab-qa-run-*/**/rspec-*.xml"
96
101
  - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
97
102
  - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$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
103
+ - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_FAILURES_REPORTING_PROJECT" $QA_FAILURES_REPORTER_OPTIONS || true; fi
98
104
  - exit $test_run_exit_code
99
105
 
100
106
  .ce-qa:
@@ -333,6 +339,7 @@ ce:update:
333
339
  - exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=CE} ${RELEASE:=CE} -- $RSPEC_REPORT_OPTS || test_run_exit_code=$?
334
340
  - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
335
341
  - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$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
342
+ - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_FAILURES_REPORTING_PROJECT" $QA_FAILURES_REPORTER_OPTIONS || true; fi
336
343
  - exit $test_run_exit_code
337
344
  extends:
338
345
  - .test
@@ -347,6 +354,7 @@ ce:update-quarantine:
347
354
  - exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=CE} ${RELEASE:=CE} -- --tag quarantine --tag ~orchestrated $RSPEC_REPORT_OPTS || test_run_exit_code=$?
348
355
  - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
349
356
  - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$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
357
+ - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_FAILURES_REPORTING_PROJECT" $QA_FAILURES_REPORTER_OPTIONS || true; fi
350
358
  - exit $test_run_exit_code
351
359
  extends:
352
360
  - .test
@@ -360,6 +368,7 @@ ee:update:
360
368
  - exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=EE} ${RELEASE:=EE} -- $RSPEC_REPORT_OPTS || test_run_exit_code=$?
361
369
  - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
362
370
  - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$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
371
+ - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_FAILURES_REPORTING_PROJECT" $QA_FAILURES_REPORTER_OPTIONS || true; fi
363
372
  - exit $test_run_exit_code
364
373
  extends:
365
374
  - .test
@@ -374,6 +383,7 @@ ee:update-quarantine:
374
383
  - exe/gitlab-qa Test::Omnibus::Update ${RELEASE:=EE} ${RELEASE:=EE} -- --tag quarantine --tag ~orchestrated $RSPEC_REPORT_OPTS || test_run_exit_code=$?
375
384
  - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
376
385
  - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$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
386
+ - if [ "$TOP_UPSTREAM_SOURCE_REF" == "master" ] || [[ "$TOP_UPSTREAM_SOURCE_JOB" == https://ops.gitlab.net* ]]; then exe/gitlab-qa-report --relate-failure-issue "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_FAILURES_REPORTING_PROJECT" $QA_FAILURES_REPORTER_OPTIONS || true; fi
377
387
  - exit $test_run_exit_code
378
388
  extends:
379
389
  - .test
@@ -729,6 +739,44 @@ ee:packages-quarantine:
729
739
  variables:
730
740
  QA_SCENARIO: "Test::Integration::Packages"
731
741
 
742
+ ce:actioncable:
743
+ extends:
744
+ - .test
745
+ - .high-capacity
746
+ - .ce-qa
747
+ - .rspec-report-opts
748
+ variables:
749
+ QA_SCENARIO: "Test::Integration::Actioncable"
750
+
751
+ ce:actioncable-quarantine:
752
+ extends:
753
+ - .test
754
+ - .high-capacity
755
+ - .ce-qa
756
+ - .quarantine
757
+ - .rspec-report-opts
758
+ variables:
759
+ QA_SCENARIO: "Test::Integration::Actioncable"
760
+
761
+ ee:actioncable:
762
+ extends:
763
+ - .test
764
+ - .high-capacity
765
+ - .ee-qa
766
+ - .rspec-report-opts
767
+ variables:
768
+ QA_SCENARIO: "Test::Integration::Actioncable"
769
+
770
+ ee:actioncable-quarantine:
771
+ extends:
772
+ - .test
773
+ - .high-capacity
774
+ - .ee-qa
775
+ - .quarantine
776
+ - .rspec-report-opts
777
+ variables:
778
+ QA_SCENARIO: "Test::Integration::Actioncable"
779
+
732
780
  ee:elasticsearch:
733
781
  extends:
734
782
  - .test
@@ -903,6 +951,22 @@ staging:
903
951
  - .only-qa
904
952
  allow_failure: true
905
953
 
954
+ generate_test_session:
955
+ stage: report
956
+ rules:
957
+ - if: '$TOP_UPSTREAM_SOURCE_JOB && $TOP_UPSTREAM_SOURCE_REF == "master"'
958
+ when: always
959
+ - if: '$TOP_UPSTREAM_SOURCE_JOB =~ /\Ahttps:\/\/ops.gitlab.net\//'
960
+ when: always
961
+ artifacts:
962
+ when: always
963
+ expire_in: 10d
964
+ paths:
965
+ - REPORT_ISSUE_URL
966
+ script:
967
+ - export GITLAB_QA_ACCESS_TOKEN="$GITLAB_QA_PRODUCTION_ACCESS_TOKEN"
968
+ - exe/gitlab-qa-report --generate-test-session "gitlab-qa-run-*/**/rspec-*.json" --project "$QA_TESTCASE_SESSIONS_PROJECT"
969
+
906
970
  .notify_upstream_commit:
907
971
  stage: notify
908
972
  image: ruby:2.6
@@ -928,7 +992,7 @@ notify_upstream_commit:failure:
928
992
  .notify_slack:
929
993
  image: alpine
930
994
  stage: notify
931
- dependencies: []
995
+ dependencies: ['generate_test_session']
932
996
  cache: {}
933
997
  before_script:
934
998
  - apk update && apk add git curl bash
@@ -947,4 +1011,4 @@ notify_slack:
947
1011
  - echo "RELEASE is ${RELEASE}"
948
1012
  - echo "CI_PIPELINE_URL is $CI_PIPELINE_URL"
949
1013
  - echo "TOP_UPSTREAM_SOURCE_JOB is $TOP_UPSTREAM_SOURCE_JOB"
950
- - bin/slack $NOTIFY_CHANNEL "☠️ QA against $RELEASE failed! ☠️ See $CI_PIPELINE_URL (triggered from $TOP_UPSTREAM_SOURCE_JOB)" ci_failing
1014
+ - 'bin/slack $NOTIFY_CHANNEL "☠️ QA against $RELEASE failed! ☠️ See the test session report: $(cat REPORT_ISSUE_URL), and pipeline: $CI_PIPELINE_URL (triggered from $TOP_UPSTREAM_SOURCE_JOB)" ci_failing'
@@ -32,4 +32,4 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-qa/commits/mast
32
32
  - Checklist after merging:
33
33
  - [ ] [Create a tag for the new release version](docs/release_process.md#how-to).
34
34
 
35
- /label ~Quality ~backstage
35
+ /label ~Quality ~"feature::maintenance"
@@ -1,3 +1,5 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  require:
2
4
  # Due to a probably bug in rubocop (https://github.com/bbatsov/rubocop/issues/5251)
3
5
  # we need to require rubocop-rspec a second time so that RSpec/FilePath does
@@ -0,0 +1,98 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2020-10-31 07:26:03 -0700 using RuboCop version 0.82.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 51
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
12
+ # URISchemes: http, https
13
+ Layout/LineLength:
14
+ Max: 210
15
+
16
+ # Offense count: 2
17
+ # Cop supports --auto-correct.
18
+ Lint/RedundantCopDisableDirective:
19
+ Exclude:
20
+ - 'lib/gitlab/qa/component/staging.rb'
21
+ - 'lib/gitlab/qa/runtime/scenario.rb'
22
+
23
+ # Offense count: 1
24
+ # Cop supports --auto-correct.
25
+ # Configuration parameters: PreferredName.
26
+ Naming/RescuedExceptionsVariableName:
27
+ Exclude:
28
+ - 'lib/gitlab/qa/component/staging.rb'
29
+
30
+ # Offense count: 1
31
+ # Cop supports --auto-correct.
32
+ Performance/RegexpMatch:
33
+ Exclude:
34
+ - 'lib/gitlab/qa/component/gitlab.rb'
35
+
36
+ # Offense count: 3
37
+ # Cop supports --auto-correct.
38
+ RSpec/EmptyLineAfterLetBlock:
39
+ Exclude:
40
+ - 'spec/gitlab/qa/support/dev_eeqa_image_spec.rb'
41
+
42
+ # Offense count: 4
43
+ # Cop supports --auto-correct.
44
+ # Configuration parameters: CustomTransform, IgnoredWords.
45
+ RSpec/ExampleWording:
46
+ Exclude:
47
+ - 'spec/gitlab/qa/component/gitlab_spec.rb'
48
+
49
+ # Offense count: 2
50
+ RSpec/LeakyConstantDeclaration:
51
+ Exclude:
52
+ - 'spec/gitlab/qa/scenario/test/instance/deployment_base_spec.rb'
53
+
54
+ # Offense count: 93
55
+ # Cop supports --auto-correct.
56
+ # Configuration parameters: EnforcedStyle.
57
+ # SupportedStyles: always, always_true, never
58
+ Style/FrozenStringLiteralComment:
59
+ Enabled: false
60
+
61
+ # Offense count: 1
62
+ # Cop supports --auto-correct.
63
+ Style/HashTransformKeys:
64
+ Exclude:
65
+ - 'lib/gitlab/qa/docker/volumes.rb'
66
+
67
+ # Offense count: 6
68
+ # Cop supports --auto-correct.
69
+ Style/IfUnlessModifier:
70
+ Exclude:
71
+ - 'lib/gitlab/qa/component/gitlab.rb'
72
+ - 'lib/gitlab/qa/release.rb'
73
+ - 'lib/gitlab/qa/runtime/env.rb'
74
+ - 'lib/gitlab/qa/runtime/scenario.rb'
75
+ - 'lib/gitlab/qa/scenario/test/omnibus/upgrade.rb'
76
+ - 'lib/gitlab/qa/support/http_request.rb'
77
+
78
+ # Offense count: 1
79
+ Style/MethodMissingSuper:
80
+ Exclude:
81
+ - 'lib/gitlab/qa/runtime/scenario.rb'
82
+
83
+ # Offense count: 1
84
+ Style/MissingRespondToMissing:
85
+ Exclude:
86
+ - 'lib/gitlab/qa/runtime/scenario.rb'
87
+
88
+ # Offense count: 5
89
+ # Cop supports --auto-correct.
90
+ # Configuration parameters: EnforcedStyle.
91
+ # SupportedStyles: literals, strict
92
+ Style/MutableConstant:
93
+ Exclude:
94
+ - 'db/migrate/**/*'
95
+ - 'db/post_migrate/**/*'
96
+ - 'db/geo/migrate/**/*'
97
+ - 'lib/gitlab/qa/release.rb'
98
+ - 'lib/gitlab/qa/report/update_screenshot_path.rb'
@@ -545,6 +545,23 @@ $ export EE_LICENSE=$(cat /path/to/GitLab.gitlab_license)
545
545
  $ gitlab-qa Test::Integration::Jira EE
546
546
  ```
547
547
 
548
+ ### `Test::Integration::Actioncable CE|EE|<full image address>`
549
+
550
+ This tests the real-time assignees feature by setting
551
+ `actioncable['enable'] = true` in the Omnibus configuration
552
+ before starting the GitLab container.
553
+
554
+ To run tests against the GitLab container, a GitLab QA (`gitlab/gitlab-qa`)
555
+ container is spun up and tests are run from it by running the
556
+ `Test::Instance::All` scenario with the `--tag actioncable` RSpec parameter,
557
+ which runs only the tests with `:actioncable` metadata.
558
+
559
+ Example:
560
+
561
+ ```
562
+ $ gitlab-qa Test::Integration::Actioncable CE
563
+ ```
564
+
548
565
  ### `Test::Instance::Any CE|EE|<full image address>:nightly|latest|any_tag http://your.instance.gitlab`
549
566
 
550
567
  This tests that a live GitLab instance works as expected by running tests
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  # Some dependencies are pinned, to prevent new cops from breaking the CI pipelines
22
22
  spec.add_development_dependency 'climate_control', '~> 0.2'
23
- spec.add_development_dependency 'gitlab-styles', '2.4.0'
23
+ spec.add_development_dependency 'gitlab-styles', '~> 4.3.0'
24
24
  spec.add_development_dependency 'pry', '~> 0.11'
25
25
  spec.add_development_dependency 'rake', '~> 12.2'
26
26
  spec.add_development_dependency 'rspec', '~> 3.7'
27
- spec.add_development_dependency 'rubocop', '~> 0.54.0'
28
- spec.add_development_dependency 'rubocop-rspec', '1.20.1'
27
+ spec.add_development_dependency 'rubocop', '~> 0.82.0'
28
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.36'
29
29
  spec.add_development_dependency 'webmock', '3.7.0'
30
30
  spec.add_runtime_dependency 'activesupport', '~> 6.0.2'
31
31
  spec.add_runtime_dependency 'gitlab', '~> 4.11.0'
@@ -39,6 +39,7 @@ module Gitlab
39
39
  end
40
40
 
41
41
  module Integration
42
+ autoload :Actioncable, 'gitlab/qa/scenario/test/integration/actioncable'
42
43
  autoload :Geo, 'gitlab/qa/scenario/test/integration/geo'
43
44
  autoload :LDAP, 'gitlab/qa/scenario/test/integration/ldap'
44
45
  autoload :LDAPNoTLS, 'gitlab/qa/scenario/test/integration/ldap_no_tls'
@@ -98,12 +99,15 @@ module Gitlab
98
99
 
99
100
  module Report
100
101
  autoload :GitlabIssueClient, 'gitlab/qa/report/gitlab_issue_client'
102
+ autoload :GitlabIssueDryClient, 'gitlab/qa/report/gitlab_issue_dry_client'
101
103
  autoload :BaseTestResults, 'gitlab/qa/report/base_test_results'
104
+ autoload :RelateFailureIssue, 'gitlab/qa/report/relate_failure_issue'
102
105
  autoload :JsonTestResults, 'gitlab/qa/report/json_test_results'
103
106
  autoload :JUnitTestResults, 'gitlab/qa/report/junit_test_results'
104
107
  autoload :PrepareStageReports, 'gitlab/qa/report/prepare_stage_reports'
105
108
  autoload :ReportAsIssue, 'gitlab/qa/report/report_as_issue'
106
109
  autoload :ResultsInIssues, 'gitlab/qa/report/results_in_issues'
110
+ autoload :GenerateTestSession, 'gitlab/qa/report/generate_test_session'
107
111
  autoload :SummaryTable, 'gitlab/qa/report/summary_table'
108
112
  autoload :TestResult, 'gitlab/qa/report/test_result'
109
113
  autoload :UpdateScreenshotPath, 'gitlab/qa/report/update_screenshot_path'
@@ -61,7 +61,7 @@ module Gitlab
61
61
  end
62
62
 
63
63
  def prepare_network
64
- if runner_network && !docker.network_exists?(runner_network)
64
+ if runner_network && !docker.network_exists?(runner_network) # rubocop:disable Style/IfUnlessModifier
65
65
  docker.network_create("--driver=bridge --internal #{runner_network}")
66
66
  end
67
67
 
@@ -30,7 +30,8 @@ module Gitlab
30
30
  begin
31
31
  run_psql 'template1'
32
32
  rescue StandardError
33
- retry if Time.now - start < 30
33
+ sleep 5
34
+ retry if Time.now - start < 60
34
35
  raise
35
36
  end
36
37
  end
@@ -27,7 +27,7 @@ module Gitlab
27
27
  end
28
28
  end
29
29
 
30
- if wait.value.exited? && wait.value.exitstatus.nonzero?
30
+ if wait.value.exited? && wait.value.exitstatus.nonzero? # rubocop:disable Style/IfUnlessModifier
31
31
  raise StatusError, "Docker command `#{@command.mask_secrets}` failed!"
32
32
  end
33
33
  end
@@ -6,8 +6,11 @@ module Gitlab
6
6
  class BaseTestResults
7
7
  include Enumerable
8
8
 
9
+ attr_reader :path
10
+
9
11
  def initialize(path)
10
- @results = parse(path)
12
+ @path = path
13
+ @results = parse
11
14
  @testcases = process
12
15
  end
13
16
 
@@ -15,7 +18,7 @@ module Gitlab
15
18
  testcases.each(&block)
16
19
  end
17
20
 
18
- def write(path)
21
+ def write
19
22
  raise NotImplementedError
20
23
  end
21
24
 
@@ -23,7 +26,7 @@ module Gitlab
23
26
 
24
27
  attr_reader :results, :testcases
25
28
 
26
- def parse(path)
29
+ def parse
27
30
  raise NotImplementedError
28
31
  end
29
32
 
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Report
6
+ class GenerateTestSession < ReportAsIssue
7
+ private
8
+
9
+ def run!
10
+ puts "Generating test results in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
11
+
12
+ tests = Dir.glob(files).flat_map do |path|
13
+ puts "Loading tests in #{path}"
14
+
15
+ Report::JsonTestResults.new(path).to_a
16
+ end
17
+
18
+ issue = gitlab.create_issue(
19
+ title: "Test session report | #{Runtime::Env.deploy_environment}",
20
+ description: generate_description(tests),
21
+ labels: ['Quality', 'QA', 'triage report']
22
+ )
23
+
24
+ File.write('REPORT_ISSUE_URL', issue.web_url)
25
+ end
26
+
27
+ def generate_description(tests)
28
+ <<~MARKDOWN
29
+ ## Session summary
30
+
31
+ * Deploy version: #{Runtime::Env.deploy_version}
32
+ * Pipeline: [#{Runtime::Env.ci_pipeline_id}](#{Runtime::Env.ci_pipeline_url})
33
+ #{generate_summary(tests: tests)}
34
+
35
+ #{generate_stages_listing(tests)}
36
+
37
+ ## Release QA issue
38
+
39
+ * #{Runtime::Env.qa_issue_url}
40
+ MARKDOWN
41
+ end
42
+
43
+ def generate_summary(tests:, tests_by_status: nil)
44
+ tests_by_status ||= tests.group_by(&:status)
45
+ total = tests.size
46
+ passed = tests_by_status['passed']&.size || 0
47
+ failed = tests_by_status['failed']&.size || 0
48
+ others = total - passed - failed
49
+
50
+ <<~MARKDOWN.chomp
51
+ * Total #{total} tests
52
+ * Passed #{passed} tests
53
+ * Failed #{failed} tests
54
+ * #{others} other tests (usually skipped)
55
+ MARKDOWN
56
+ end
57
+
58
+ def generate_stages_listing(tests)
59
+ generate_tests_by_stage(tests).map do |stage, tests_for_stage|
60
+ tests_by_status = tests_for_stage.group_by(&:status)
61
+
62
+ <<~MARKDOWN.chomp
63
+ ### #{stage&.capitalize || 'Unknown'}
64
+
65
+ #{generate_summary(
66
+ tests: tests_for_stage, tests_by_status: tests_by_status)}
67
+
68
+ #{generate_testcase_listing_by_status(
69
+ tests: tests_for_stage, tests_by_status: tests_by_status)}
70
+ MARKDOWN
71
+ end.join("\n\n")
72
+ end
73
+
74
+ def generate_tests_by_stage(tests)
75
+ # https://about.gitlab.com/handbook/product/product-categories/#devops-stages
76
+ ordering = %w[
77
+ manage
78
+ plan
79
+ create
80
+ verify
81
+ package
82
+ release
83
+ configure
84
+ monitor
85
+ secure
86
+ defend
87
+ growth
88
+ fulfillment
89
+ enablement
90
+ ]
91
+
92
+ tests.sort_by do |test|
93
+ ordering.index(test.stage) || ordering.size
94
+ end.group_by(&:stage)
95
+ end
96
+
97
+ def generate_testcase_listing_by_status(tests:, tests_by_status:)
98
+ failed_tests = tests_by_status['failed']
99
+ passed_tests = tests_by_status['passed']
100
+ other_tests = tests.reject do |test|
101
+ test.status == 'failed' || test.status == 'passed'
102
+ end
103
+
104
+ [
105
+ (failed_listings(failed_tests) if failed_tests),
106
+ (passed_listings(passed_tests) if passed_tests),
107
+ (other_listings(other_tests) if other_tests.any?)
108
+ ].compact.join("\n\n")
109
+ end
110
+
111
+ def failed_listings(failed_tests)
112
+ generate_testcase_listing(failed_tests)
113
+ end
114
+
115
+ def passed_listings(passed_tests)
116
+ <<~MARKDOWN.chomp
117
+ <details><summary>Passed tests:</summary>
118
+
119
+ #{generate_testcase_listing(passed_tests)}
120
+
121
+ </details>
122
+ MARKDOWN
123
+ end
124
+
125
+ def other_listings(other_tests)
126
+ <<~MARKDOWN.chomp
127
+ <details><summary>Other tests:</summary>
128
+
129
+ #{generate_testcase_listing(other_tests)}
130
+
131
+ </details>
132
+ MARKDOWN
133
+ end
134
+
135
+ def generate_testcase_listing(tests)
136
+ body = tests.group_by(&:testcase).map do |testcase, tests_with_same_testcase|
137
+ tests_with_same_testcase.sort_by!(&:name)
138
+ [
139
+ generate_test_text(testcase, tests_with_same_testcase),
140
+ generate_test_job(tests_with_same_testcase),
141
+ generate_test_status(tests_with_same_testcase),
142
+ generate_test_actions(tests_with_same_testcase)
143
+ ].join(' | ')
144
+ end.join("\n")
145
+
146
+ <<~MARKDOWN.chomp
147
+ | Test | Job | Status | Action |
148
+ | - | - | - | - |
149
+ #{body}
150
+ MARKDOWN
151
+ end
152
+
153
+ def generate_test_text(testcase, tests_with_same_testcase)
154
+ text = tests_with_same_testcase.map(&:name).uniq.join(', ')
155
+
156
+ if testcase
157
+ "[#{text}](#{testcase})"
158
+ else
159
+ text
160
+ end
161
+ end
162
+
163
+ def generate_test_job(tests_with_same_testcase)
164
+ tests_with_same_testcase.map(&:ci_job_url).uniq.map do |ci_job_url|
165
+ ci_job_id = ci_job_url[/\d+\z/]
166
+
167
+ "[#{ci_job_id}](#{ci_job_url})"
168
+ end.join(', ')
169
+ end
170
+
171
+ def generate_test_status(tests_with_same_testcase)
172
+ tests_with_same_testcase.map(&:status).uniq.map do |status|
173
+ %(~"#{status}")
174
+ end.join(', ')
175
+ end
176
+
177
+ def generate_test_actions(tests_with_same_testcase)
178
+ # All failed tests would be grouped together, meaning that
179
+ # if one failed, all the tests here would be failed too.
180
+ # So this check is safe. Same applies to 'passed'.
181
+ # But all other status might be mixing together,
182
+ # we cannot assume other statuses.
183
+ if tests_with_same_testcase.first.status == 'failed'
184
+ tests_having_failure_issue =
185
+ tests_with_same_testcase.select(&:failure_issue)
186
+
187
+ if tests_having_failure_issue.any?
188
+ items = tests_having_failure_issue.map do |test|
189
+ "<li>[ ] [failure issue](#{test.failure_issue})</li>"
190
+ end.join(' ')
191
+
192
+ "<ul>#{items}</ul>"
193
+ else
194
+ '<ul><li>[ ] failure issue exists or was created</li></ul>'
195
+ end
196
+ else
197
+ '-'
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end