gitlab-qa 6.4.0 → 6.8.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 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