diff options
Diffstat (limited to 'qa')
139 files changed, 1955 insertions, 1267 deletions
diff --git a/qa/Gemfile b/qa/Gemfile index cf939f8e301..12e5d66fc6b 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -2,46 +2,48 @@ source 'https://rubygems.org' -gem 'gitlab-qa', '~> 8', require: 'gitlab/qa' +gem 'gitlab-qa', '~> 8', '>= 8.8.0', require: 'gitlab/qa' gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile -gem 'allure-rspec', '~> 2.16.0' -gem 'capybara', '~> 3.35.0' -gem 'capybara-screenshot', '~> 1.0.23' +gem 'allure-rspec', '~> 2.18.0' +gem 'capybara', '~> 3.37.1' +gem 'capybara-screenshot', '~> 1.0.26' gem 'rake', '~> 13' -gem 'rspec', '~> 3.10' -gem 'selenium-webdriver', '~> 4.0' -gem 'airborne', '~> 0.3.4', require: false # airborne is messing with rspec sandboxed mode so not requiring by default +gem 'rspec', '~> 3.11' +gem 'selenium-webdriver', '~> 4.5' +gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default gem 'rest-client', '~> 2.1.0' gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry' -gem 'rspec_junit_formatter', '~> 0.4.1' -gem 'faker', '~> 2.19', '>= 2.19.0' +gem 'rspec_junit_formatter', '~> 0.6.0' +gem 'faker', '~> 2.23' gem 'knapsack', '~> 4.0' -gem 'parallel_tests', '~> 2.29' -gem 'rotp', '~> 3.1.0' -gem 'timecop', '~> 0.9.1' +gem 'parallel_tests', '~> 3.13' +gem 'rotp', '~> 6.2.0' +gem 'timecop', '~> 0.9.5' gem 'parallel', '~> 1.19' gem 'rainbow', '~> 3.0.0' -gem 'rspec-parameterized', '~> 0.4.2' -gem 'octokit', '~> 4.21' -gem 'webdrivers', '~> 5.0' +gem 'rspec-parameterized', '~> 0.5.2' +gem 'octokit', '~> 5.6.1' +gem "faraday-retry", "~> 2.0" +gem 'webdrivers', '~> 5.2' gem 'zeitwerk', '~> 2.4' gem 'influxdb-client', '~> 1.17' -gem 'terminal-table', '~> 3.0.0', require: false +gem 'terminal-table', '~> 3.0.2', require: false gem 'slack-notifier', '~> 2.4', require: false -gem 'fog-google', '~> 1.17', require: false +gem 'fog-google', '~> 1.19', require: false +gem 'fog-core', '2.1.0', require: false # fog-google generates a ton of warnings with latest core gem "warning", "~> 1.3" gem 'confiner', '~> 0.3' -gem 'chemlab', '~> 0.9' +gem 'chemlab', '~> 0.10' gem 'chemlab-library-www-gitlab-com', '~> 0.1' # dependencies for jenkins client -gem 'nokogiri', '~> 1.12' +gem 'nokogiri', '~> 1.13', '>= 1.13.9' -gem 'deprecation_toolkit', '~> 1.5.1', require: false +gem 'deprecation_toolkit', '~> 2.0.0', require: false group :development do - gem 'pry-byebug', '~> 3.5.1', platform: :mri - gem "ruby-debug-ide", "~> 0.7.0" + gem 'pry-byebug', '~> 3.10.1', platform: :mri + gem "ruby-debug-ide", "~> 0.7.3" end diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index dd14b675769..23f82f553f1 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -1,28 +1,24 @@ GEM remote: https://rubygems.org/ specs: - abstract_type (0.0.7) activesupport (6.1.4.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - adamantium (0.2.0) - ice_nine (~> 0.11.0) - memoizable (~> 0.4.0) addressable (2.8.1) public_suffix (>= 2.0.2, < 6.0) - airborne (0.3.4) + airborne (0.3.7) activesupport rack rack-test (>= 1.1.0, < 2.0) rest-client (>= 2.0.2, < 3.0) rspec (~> 3.8) - allure-rspec (2.16.1) - allure-ruby-commons (= 2.16.1) + allure-rspec (2.18.0) + allure-ruby-commons (= 2.18.0) rspec-core (>= 3.8, < 4) - allure-ruby-commons (2.16.1) + allure-ruby-commons (2.18.0) mime-types (>= 3.3, < 4) oj (>= 3.10, < 4) require_all (>= 2, < 4) @@ -30,19 +26,20 @@ GEM ast (2.4.2) binding_ninja (0.2.3) builder (3.2.4) - byebug (9.1.0) - capybara (3.35.3) + byebug (11.1.3) + capybara (3.37.1) addressable + matrix mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - capybara-screenshot (1.0.23) + capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - chemlab (0.9.2) + chemlab (0.10.0) colorize (~> 0.8) i18n (~> 1.8) rake (>= 12, < 14) @@ -53,40 +50,25 @@ GEM childprocess (4.1.0) coderay (1.1.2) colorize (0.8.1) - concord (0.1.5) - adamantium (~> 0.2.0) - equalizer (~> 0.0.9) concurrent-ruby (1.1.10) confiner (0.3.0) gitlab (>= 4.17) zeitwerk (~> 2.5.1) declarative (0.0.20) - deprecation_toolkit (1.5.1) - activesupport (>= 4.2) + deprecation_toolkit (2.0.0) + activesupport (>= 5.2) diff-lcs (1.3) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - equalizer (0.0.11) - excon (0.88.0) - faker (2.19.0) - i18n (>= 1.6, < 2) - faraday (1.5.1) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - multipart-post (>= 1.2, < 3) + excon (0.92.4) + faker (2.23.0) + i18n (>= 1.8.11, < 2) + faraday (2.5.2) + faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) + faraday-net_http (3.0.0) + faraday-retry (2.0.0) + faraday (~> 2.0) ffi (1.15.5) ffi-compiler (1.0.1) ffi (>= 1.0.0) @@ -96,8 +78,8 @@ GEM excon (~> 0.58) formatador (~> 0.2) mime-types - fog-google (1.17.0) - fog-core (<= 2.1.0) + fog-google (1.19.0) + fog-core (< 2.3) fog-json (~> 1.2) fog-xml (~> 0.1.0) google-apis-compute_v1 (~> 0.14) @@ -118,17 +100,18 @@ GEM gitlab (4.18.0) httparty (~> 0.18) terminal-table (>= 1.5.1) - gitlab-qa (8.4.2) + gitlab-qa (8.8.0) activesupport (~> 6.1) gitlab (~> 4.18.0) http (~> 5.0) nokogiri (~> 1.10) - rainbow (~> 3.0.0) + rainbow (>= 3, < 4) table_print (= 1.5.7) - zeitwerk (~> 2.4) - google-apis-compute_v1 (0.21.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) + toxiproxy (~> 2.0.2) + zeitwerk (>= 2, < 3) + google-apis-compute_v1 (0.51.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-core (0.9.0) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -137,22 +120,22 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-dns_v1 (0.16.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-iamcredentials_v1 (0.8.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-monitoring_v3 (0.18.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-pubsub_v1 (0.10.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-sqladmin_v1beta4 (0.21.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.9.0) - google-apis-core (>= 0.4, < 2.a) + google-apis-dns_v1 (0.27.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-iamcredentials_v1 (0.14.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-monitoring_v3 (0.33.0) + google-apis-core (>= 0.7, < 2.a) + google-apis-pubsub_v1 (0.28.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-sqladmin_v1beta4 (0.36.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-storage_v1 (0.18.0) + google-apis-core (>= 0.7, < 2.a) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - googleauth (1.1.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.2.0) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) @@ -173,22 +156,20 @@ GEM httpclient (2.8.3) i18n (1.12.0) concurrent-ruby (~> 1.0) - ice_nine (0.11.2) influxdb-client (1.17.0) - jwt (2.3.0) + jwt (2.5.0) knapsack (4.0.0) rake - launchy (2.4.3) - addressable (~> 2.3) + launchy (2.5.0) + addressable (~> 2.7) llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) rake (~> 13.0) macaddr (1.7.2) systemu (~> 2.6.5) + matrix (0.4.2) memoist (0.16.2) - memoizable (0.4.2) - thread_safe (~> 0.3, >= 0.3.1) - method_source (0.9.0) + method_source (1.0.0) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) @@ -197,18 +178,17 @@ GEM minitest (5.16.3) multi_json (1.15.0) multi_xml (0.6.0) - multipart-post (2.1.1) netrc (0.11.0) - nokogiri (1.13.8) + nokogiri (1.13.9) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.25.1) + octokit (5.6.1) faraday (>= 1, < 3) sawyer (~> 0.9) - oj (3.13.11) + oj (3.13.21) os (1.1.4) parallel (1.19.2) - parallel_tests (2.29.0) + parallel_tests (3.13.0) parallel parser (3.1.2.1) ast (~> 2.4.1) @@ -216,13 +196,12 @@ GEM coderay parser unparser - procto (0.0.3) - pry (0.11.3) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - pry-byebug (3.5.1) - byebug (~> 9.1) - pry (~> 0.10) + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.10.1) + byebug (~> 11.0) + pry (>= 0.13, < 0.15) public_suffix (5.0.0) racc (1.6.0) rack (2.2.3.1) @@ -231,7 +210,7 @@ GEM rainbow (3.0.0) rake (13.0.6) regexp_parser (2.1.1) - representable (3.1.1) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) @@ -243,44 +222,45 @@ GEM netrc (~> 0.8) retriable (3.1.2) rexml (3.2.5) - rotp (3.1.0) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rotp (6.2.0) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-parameterized (0.4.2) + rspec-support (~> 3.11.0) + rspec-parameterized (0.5.2) binding_ninja (>= 0.2.3) parser proc_to_ast rspec (>= 2.13, < 4) unparser - rspec-retry (0.6.1) + rspec-retry (0.6.2) rspec-core (> 3.3) - rspec-support (3.10.2) - rspec_junit_formatter (0.4.1) + rspec-support (3.11.1) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - ruby-debug-ide (0.7.2) + ruby-debug-ide (0.7.3) rake (>= 0.8.1) - ruby2_keywords (0.0.4) + ruby2_keywords (0.0.5) rubyzip (2.3.2) sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - selenium-webdriver (4.0.3) + selenium-webdriver (4.5.0) childprocess (>= 0.5, < 5.0) rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2) - signet (0.16.0) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + signet (0.17.0) addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) slack-notifier (2.4.0) @@ -288,8 +268,8 @@ GEM table_print (1.5.7) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thread_safe (0.3.6) - timecop (0.9.1) + timecop (0.9.5) + toxiproxy (2.0.2) trailblazer-option (0.1.2) tzinfo (2.0.5) concurrent-ruby (~> 1.0) @@ -297,26 +277,22 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (2.2.0) - unparser (0.4.7) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - concord (~> 0.1.5) + unicode-display_width (2.3.0) + unparser (0.6.5) diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - parser (>= 2.6.5) - procto (~> 0.0.2) + parser (>= 3.1.0) uuid (2.3.9) macaddr (~> 1.0) warning (1.3.0) - watir (6.19.1) + watir (7.1.0) regexp_parser (>= 1.2, < 3) - selenium-webdriver (>= 3.142.7) - webdrivers (5.0.0) + selenium-webdriver (~> 4.0) + webdrivers (5.2.0) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (~> 4.0) webrick (1.7.0) + websocket (1.2.9) xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.5.4) @@ -326,40 +302,42 @@ PLATFORMS DEPENDENCIES activesupport (~> 6.1.4.7) - airborne (~> 0.3.4) - allure-rspec (~> 2.16.0) - capybara (~> 3.35.0) - capybara-screenshot (~> 1.0.23) - chemlab (~> 0.9) + airborne (~> 0.3.7) + allure-rspec (~> 2.18.0) + capybara (~> 3.37.1) + capybara-screenshot (~> 1.0.26) + chemlab (~> 0.10) chemlab-library-www-gitlab-com (~> 0.1) confiner (~> 0.3) - deprecation_toolkit (~> 1.5.1) - faker (~> 2.19, >= 2.19.0) - fog-google (~> 1.17) - gitlab-qa (~> 8) + deprecation_toolkit (~> 2.0.0) + faker (~> 2.23) + faraday-retry (~> 2.0) + fog-core (= 2.1.0) + fog-google (~> 1.19) + gitlab-qa (~> 8, >= 8.8.0) influxdb-client (~> 1.17) knapsack (~> 4.0) - nokogiri (~> 1.12) - octokit (~> 4.21) + nokogiri (~> 1.13, >= 1.13.9) + octokit (~> 5.6.1) parallel (~> 1.19) - parallel_tests (~> 2.29) - pry-byebug (~> 3.5.1) + parallel_tests (~> 3.13) + pry-byebug (~> 3.10.1) rainbow (~> 3.0.0) rake (~> 13) rest-client (~> 2.1.0) - rotp (~> 3.1.0) - rspec (~> 3.10) - rspec-parameterized (~> 0.4.2) + rotp (~> 6.2.0) + rspec (~> 3.11) + rspec-parameterized (~> 0.5.2) rspec-retry (~> 0.6.1) - rspec_junit_formatter (~> 0.4.1) - ruby-debug-ide (~> 0.7.0) - selenium-webdriver (~> 4.0) + rspec_junit_formatter (~> 0.6.0) + ruby-debug-ide (~> 0.7.3) + selenium-webdriver (~> 4.5) slack-notifier (~> 2.4) - terminal-table (~> 3.0.0) - timecop (~> 0.9.1) + terminal-table (~> 3.0.2) + timecop (~> 0.9.5) warning (~> 1.3) - webdrivers (~> 5.0) + webdrivers (~> 5.2) zeitwerk (~> 2.4) BUNDLED WITH - 2.3.15 + 2.3.24 diff --git a/qa/Rakefile b/qa/Rakefile index ada27596ae4..6f94c63b4de 100644 --- a/qa/Rakefile +++ b/qa/Rakefile @@ -11,7 +11,7 @@ end desc "Initialize GitLab with an access token" task :initialize_gitlab_auth, [:address] do |_, args| - QA::Tools::InitializeGitLabAuth.new(args).run + QA::Tools::InitializeGitlabAuth.new(args).run end desc "Generate Performance Testdata" @@ -46,7 +46,7 @@ task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_l desc "Deletes test ssh keys a user" task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |_, args| - QA::Tools::DeleteTestSSHKeys.new(args).run + QA::Tools::DeleteTestSshKeys.new(args).run end desc "Deletes projects directly under the provided group" diff --git a/qa/chemlab-library-gitlab.gemspec b/qa/chemlab-library-gitlab.gemspec index 9af4a650d98..309de157757 100644 --- a/qa/chemlab-library-gitlab.gemspec +++ b/qa/chemlab-library-gitlab.gemspec @@ -1,6 +1,6 @@ # frozen_string_literal: true -$:.unshift(File.expand_path('lib', __dir__)) +$LOAD_PATH.unshift(File.expand_path('lib', __dir__)) Gem::Specification.new do |spec| spec.name = 'chemlab-library-gitlab' diff --git a/qa/lib/gitlab/page/admin/subscription.rb b/qa/lib/gitlab/page/admin/subscription.rb index ef73bad2879..058cf8d281e 100644 --- a/qa/lib/gitlab/page/admin/subscription.rb +++ b/qa/lib/gitlab/page/admin/subscription.rb @@ -23,7 +23,7 @@ module Gitlab h2 :users_over_subscription table :subscription_history - span :no_valid_license_alert, text: /no longer has a valid license/ + div :no_valid_license_alert, text: /no longer has a valid license/ h3 :no_active_subscription_title, text: /do not have an active subscription/ def accept_terms diff --git a/qa/lib/gitlab/page/group/settings/usage_quota.stub.rb b/qa/lib/gitlab/page/group/settings/usage_quota.stub.rb index 192e71e6c90..2a5d9a6bb5e 100644 --- a/qa/lib/gitlab/page/group/settings/usage_quota.stub.rb +++ b/qa/lib/gitlab/page/group/settings/usage_quota.stub.rb @@ -5,51 +5,51 @@ module Gitlab module Group module Settings module UsageQuota - # @note Defined as +link :pipeline_tab+ - # Clicks +pipeline_tab+ - def pipeline_tab + # @note Defined as +link :seats_tab+ + # Clicks +seats_tab+ + def seats_tab # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.pipeline_tab_element).to exist + # expect(usage_quota.seats_tab_element).to exist # end # @return [Watir::Link] The raw +Link+ element - def pipeline_tab_element + def seats_tab_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_pipeline_tab + # expect(usage_quota).to be_seats_tab # end - # @return [Boolean] true if the +pipeline_tab+ element is present on the page - def pipeline_tab? + # @return [Boolean] true if the +seats_tab+ element is present on the page + def seats_tab? # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +link :storage_tab+ - # Clicks +storage_tab+ - def storage_tab + # @note Defined as +link :pipelines_tab+ + # Clicks +pipelines_tab+ + def pipelines_tab # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.storage_tab_element).to exist + # expect(usage_quota.pipelines_tab_element).to exist # end # @return [Watir::Link] The raw +Link+ element - def storage_tab_element + def pipelines_tab_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_storage_tab + # expect(usage_quota).to be_pipelines_tab # end - # @return [Boolean] true if the +storage_tab+ element is present on the page - def storage_tab? + # @return [Boolean] true if the +pipelines_tab+ element is present on the page + def pipelines_tab? # This is a stub, used for indexing. The method is dynamically generated. end @@ -77,6 +77,102 @@ module Gitlab # This is a stub, used for indexing. The method is dynamically generated. end + # @note Defined as +div :plan_ci_minutes+ + # @return [String] The text content or value of +plan_ci_minutes+ + def plan_ci_minutes + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.plan_ci_minutes_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def plan_ci_minutes_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_plan_ci_minutes + # end + # @return [Boolean] true if the +plan_ci_minutes+ element is present on the page + def plan_ci_minutes? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :additional_ci_minutes+ + # @return [String] The text content or value of +additional_ci_minutes+ + def additional_ci_minutes + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.additional_ci_minutes_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def additional_ci_minutes_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_additional_ci_minutes + # end + # @return [Boolean] true if the +additional_ci_minutes+ element is present on the page + def additional_ci_minutes? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :ci_purchase_successful_alert+ + # @return [String] The text content or value of +ci_purchase_successful_alert+ + def ci_purchase_successful_alert + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.ci_purchase_successful_alert_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def ci_purchase_successful_alert_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_ci_purchase_successful_alert + # end + # @return [Boolean] true if the +ci_purchase_successful_alert+ element is present on the page + def ci_purchase_successful_alert? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +link :storage_tab+ + # Clicks +storage_tab+ + def storage_tab + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.storage_tab_element).to exist + # end + # @return [Watir::Link] The raw +Link+ element + def storage_tab_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_storage_tab + # end + # @return [Boolean] true if the +storage_tab+ element is present on the page + def storage_tab? + # This is a stub, used for indexing. The method is dynamically generated. + end + # @note Defined as +link :buy_storage+ # Clicks +buy_storage+ def buy_storage @@ -101,123 +197,315 @@ module Gitlab # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +strong :additional_minutes+ - # @return [String] The text content or value of +additional_minutes+ - def additional_minutes + # @note Defined as +div :used_storage_message+ + # @return [String] The text content or value of +used_storage_message+ + def used_storage_message + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.used_storage_message_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def used_storage_message_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_used_storage_message + # end + # @return [Boolean] true if the +used_storage_message+ element is present on the page + def used_storage_message? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :group_usage_message+ + # @return [String] The text content or value of +group_usage_message+ + def group_usage_message + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.group_usage_message_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def group_usage_message_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_group_usage_message + # end + # @return [Boolean] true if the +group_usage_message+ element is present on the page + def group_usage_message? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :dependency_proxy_usage+ + # @return [String] The text content or value of +dependency_proxy_usage+ + def dependency_proxy_usage + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.dependency_proxy_usage_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def dependency_proxy_usage_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_dependency_proxy_usage + # end + # @return [Boolean] true if the +dependency_proxy_usage+ element is present on the page + def dependency_proxy_usage? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +span :dependency_proxy_size+ + # @return [String] The text content or value of +dependency_proxy_size+ + def dependency_proxy_size # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.additional_minutes_element).to exist + # expect(usage_quota.dependency_proxy_size_element).to exist # end - # @return [Watir::Strong] The raw +Strong+ element - def additional_minutes_element + # @return [Watir::Span] The raw +Span+ element + def dependency_proxy_size_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_additional_minutes + # expect(usage_quota).to be_dependency_proxy_size # end - # @return [Boolean] true if the +additional_minutes+ element is present on the page - def additional_minutes? + # @return [Boolean] true if the +dependency_proxy_size+ element is present on the page + def dependency_proxy_size? # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +div :additional_minutes_usage+ - # @return [String] The text content or value of +additional_minutes_usage+ - def additional_minutes_usage + # @note Defined as +div :container_registry_usage+ + # @return [String] The text content or value of +container_registry_usage+ + def container_registry_usage # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.additional_minutes_usage_element).to exist + # expect(usage_quota.container_registry_usage_element).to exist # end # @return [Watir::Div] The raw +Div+ element - def additional_minutes_usage_element + def container_registry_usage_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_additional_minutes_usage + # expect(usage_quota).to be_container_registry_usage # end - # @return [Boolean] true if the +additional_minutes_usage+ element is present on the page - def additional_minutes_usage? + # @return [Boolean] true if the +container_registry_usage+ element is present on the page + def container_registry_usage? # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +strong :plan_minutes+ - # @return [String] The text content or value of +plan_minutes+ - def plan_minutes + # @note Defined as +div :project_storage_used+ + # @return [String] The text content or value of +project_storage_used+ + def project_storage_used # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.plan_minutes_element).to exist + # expect(usage_quota.project_storage_used_element).to exist # end - # @return [Watir::Strong] The raw +Strong+ element - def plan_minutes_element + # @return [Watir::Div] The raw +Div+ element + def project_storage_used_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_plan_minutes + # expect(usage_quota).to be_project_storage_used # end - # @return [Boolean] true if the +plan_minutes+ element is present on the page - def plan_minutes? + # @return [Boolean] true if the +project_storage_used+ element is present on the page + def project_storage_used? # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +div :plan_minutes_usage+ - # @return [String] The text content or value of +plan_minutes_usage+ - def plan_minutes_usage + # @note Defined as +div :project+ + # @return [String] The text content or value of +project+ + def project # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.plan_minutes_usage_element).to exist + # expect(usage_quota.project_element).to exist # end # @return [Watir::Div] The raw +Div+ element - def plan_minutes_usage_element + def project_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_plan_minutes_usage + # expect(usage_quota).to be_project # end - # @return [Boolean] true if the +plan_minutes_usage+ element is present on the page - def plan_minutes_usage? + # @return [Boolean] true if the +project+ element is present on the page + def project? # This is a stub, used for indexing. The method is dynamically generated. end - # @note Defined as +div :purchase_successful_alert+ - # @return [String] The text content or value of +purchase_successful_alert+ - def purchase_successful_alert + # @note Defined as +div :storage_type_legend+ + # @return [String] The text content or value of +storage_type_legend+ + def storage_type_legend # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota.purchase_successful_alert_element).to exist + # expect(usage_quota.storage_type_legend_element).to exist # end # @return [Watir::Div] The raw +Div+ element - def purchase_successful_alert_element + def storage_type_legend_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_storage_type_legend + # end + # @return [Boolean] true if the +storage_type_legend+ element is present on the page + def storage_type_legend? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +span :container_registry_size+ + # @return [String] The text content or value of +container_registry_size+ + def container_registry_size + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.container_registry_size_element).to exist + # end + # @return [Watir::Span] The raw +Span+ element + def container_registry_size_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_container_registry_size + # end + # @return [Boolean] true if the +container_registry_size+ element is present on the page + def container_registry_size? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :purchased_usage_total_free+ + # @return [String] The text content or value of +purchased_usage_total_free+ + def purchased_usage_total_free + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.purchased_usage_total_free_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def purchased_usage_total_free_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_purchased_usage_total_free + # end + # @return [Boolean] true if the +purchased_usage_total_free+ element is present on the page + def purchased_usage_total_free? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +span :purchased_usage_total+ + # @return [String] The text content or value of +purchased_usage_total+ + def purchased_usage_total + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.purchased_usage_total_element).to exist + # end + # @return [Watir::Span] The raw +Span+ element + def purchased_usage_total_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_purchased_usage_total + # end + # @return [Boolean] true if the +purchased_usage_total+ element is present on the page + def purchased_usage_total? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +div :storage_purchase_successful_alert+ + # @return [String] The text content or value of +storage_purchase_successful_alert+ + def storage_purchase_successful_alert + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.storage_purchase_successful_alert_element).to exist + # end + # @return [Watir::Div] The raw +Div+ element + def storage_purchase_successful_alert_element + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota).to be_storage_purchase_successful_alert + # end + # @return [Boolean] true if the +storage_purchase_successful_alert+ element is present on the page + def storage_purchase_successful_alert? + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @note Defined as +h2 :storage_available_alert+ + # @return [String] The text content or value of +storage_available_alert+ + def storage_available_alert + # This is a stub, used for indexing. The method is dynamically generated. + end + + # @example + # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| + # expect(usage_quota.storage_available_alert_element).to exist + # end + # @return [Watir::H2] The raw +H2+ element + def storage_available_alert_element # This is a stub, used for indexing. The method is dynamically generated. end # @example # Gitlab::Page::Group::Settings::UsageQuota.perform do |usage_quota| - # expect(usage_quota).to be_purchase_successful_alert + # expect(usage_quota).to be_storage_available_alert # end - # @return [Boolean] true if the +purchase_successful_alert+ element is present on the page - def purchase_successful_alert? + # @return [Boolean] true if the +storage_available_alert+ element is present on the page + def storage_available_alert? # This is a stub, used for indexing. The method is dynamically generated. end end diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb index 2b491188595..a1ab345bf1e 100644 --- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb +++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb @@ -5,16 +5,30 @@ module Gitlab module Group module Settings class UsageQuotas < Chemlab::Page - # TODO: Supplant with data-qa-selectors + # Seats section + link :seats_tab + + # Pipelines section link :pipelines_tab - link :storage_tab link :buy_ci_minutes - link :buy_storage div :plan_ci_minutes div :additional_ci_minutes - span :purchased_usage_total - div :purchased_usage_total_free, 'data-testid': 'purchased-usage-card' # Different UI for free namespace div :ci_purchase_successful_alert, text: /You have successfully purchased CI minutes/ + + # Storage section + link :storage_tab + link :purchase_more_storage + div :used_storage_message + div :group_usage_message + div :dependency_proxy_usage + span :dependency_proxy_size + div :container_registry_usage + div :project_storage_used + div :project + div :storage_type_legend + span :container_registry_size + div :purchased_usage_total_free # Different UI for free namespace + span :purchased_usage_total div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/ h2 :storage_available_alert, text: /purchased storage is available/ @@ -35,6 +49,15 @@ module Gitlab false end + # Waits and Checks if storage project data loaded + # + # @return [Boolean] True if the alert presents, false if not after 5 second wait + def project_storage_data_available? + storage_type_legend_element.wait_until(timeout: 3, &:present?) + rescue Watir::Wait::TimeoutError + false + end + # Returns total purchased storage value once it's ready on page # # @return [Float] Total purchased storage value in GiB @@ -30,6 +30,22 @@ module QA loader.ignore("#{root}/specs/features") loader.ignore("#{root}/specs/spec_helper.rb") + # we need to eager load scenario classes + # zeitwerk does not have option to configure what to eager load, so all exceptions have to be defined + loader.do_not_eager_load("#{root}/ce") + loader.do_not_eager_load("#{root}/ee") + loader.do_not_eager_load("#{root}/flow") + loader.do_not_eager_load("#{root}/git") + loader.do_not_eager_load("#{root}/mobile") + loader.do_not_eager_load("#{root}/page") + loader.do_not_eager_load("#{root}/resource") + loader.do_not_eager_load("#{root}/runtime") + loader.do_not_eager_load("#{root}/service") + loader.do_not_eager_load("#{root}/specs") + loader.do_not_eager_load("#{root}/support") + loader.do_not_eager_load("#{root}/tools") + loader.do_not_eager_load("#{root}/vendor") + loader.inflector.inflect( "ce" => "CE", "ee" => "EE", @@ -74,6 +90,7 @@ module QA end loader.setup + loader.eager_load end # Custom warning processing diff --git a/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb b/qa/qa/fixtures/package_managers/maven/gradle/build_install.gradle.erb index 31543d30e88..31543d30e88 100644 --- a/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb +++ b/qa/qa/fixtures/package_managers/maven/gradle/build_install.gradle.erb diff --git a/qa/qa/fixtures/package_managers/maven/build_upload.gradle.erb b/qa/qa/fixtures/package_managers/maven/gradle/build_upload.gradle.erb index c14e63e11df..c14e63e11df 100644 --- a/qa/qa/fixtures/package_managers/maven/build_upload.gradle.erb +++ b/qa/qa/fixtures/package_managers/maven/gradle/build_upload.gradle.erb diff --git a/qa/qa/fixtures/package_managers/maven/gradle_install_package.yaml.erb b/qa/qa/fixtures/package_managers/maven/gradle/gradle_install_package.yaml.erb index 49873f124cc..49873f124cc 100644 --- a/qa/qa/fixtures/package_managers/maven/gradle_install_package.yaml.erb +++ b/qa/qa/fixtures/package_managers/maven/gradle/gradle_install_package.yaml.erb diff --git a/qa/qa/fixtures/package_managers/maven/gradle_upload_package.yaml.erb b/qa/qa/fixtures/package_managers/maven/gradle/gradle_upload_package.yaml.erb index 3f3c7dce03c..3f3c7dce03c 100644 --- a/qa/qa/fixtures/package_managers/maven/gradle_upload_package.yaml.erb +++ b/qa/qa/fixtures/package_managers/maven/gradle/gradle_upload_package.yaml.erb diff --git a/qa/qa/fixtures/package_managers/maven/maven_install_package.yaml.erb b/qa/qa/fixtures/package_managers/maven/group/consumer/gitlab_ci.yaml.erb index 78d6255e9a9..78d6255e9a9 100644 --- a/qa/qa/fixtures/package_managers/maven/maven_install_package.yaml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/consumer/gitlab_ci.yaml.erb diff --git a/qa/qa/fixtures/package_managers/maven/client_pom.xml.erb b/qa/qa/fixtures/package_managers/maven/group/consumer/pom.xml.erb index 20bb5f3964e..adc8b0294b3 100644 --- a/qa/qa/fixtures/package_managers/maven/client_pom.xml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/consumer/pom.xml.erb @@ -5,8 +5,8 @@ <modelVersion>4.0.0</modelVersion> <repositories> <repository> - <id><%= package_project.name %></id> - <url><%= gitlab_address_with_port %>/api/v4/groups/<%= package_project.group.id %>/-/packages/maven</url> + <id><%= client_project.name %></id> + <url><%= gitlab_address_with_port %>/api/v4/groups/<%= client_project.group.id %>/-/packages/maven</url> </repository> </repositories> <dependencies> diff --git a/qa/qa/fixtures/package_managers/maven/group/consumer/settings.xml.erb b/qa/qa/fixtures/package_managers/maven/group/consumer/settings.xml.erb new file mode 100644 index 00000000000..fb7e2a4af88 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/group/consumer/settings.xml.erb @@ -0,0 +1,16 @@ +<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> +<servers> + <server> + <id><%= client_project.name %></id> + <configuration> + <httpHeaders> + <property> + <name><%= maven_header_name %></name> + <value><%= token %></value> + </property> + </httpHeaders> + </configuration> + </server> +</servers> +</settings>
\ No newline at end of file diff --git a/qa/qa/fixtures/package_managers/maven/maven_upload_package.yaml.erb b/qa/qa/fixtures/package_managers/maven/group/producer/gitlab_ci.yaml.erb index 64a63bf0bd8..64a63bf0bd8 100644 --- a/qa/qa/fixtures/package_managers/maven/maven_upload_package.yaml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/producer/gitlab_ci.yaml.erb diff --git a/qa/qa/fixtures/package_managers/maven/package_pom.xml.erb b/qa/qa/fixtures/package_managers/maven/group/producer/pom.xml.erb index 5159172a170..5159172a170 100644 --- a/qa/qa/fixtures/package_managers/maven/package_pom.xml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/producer/pom.xml.erb diff --git a/qa/qa/fixtures/package_managers/maven/settings.xml.erb b/qa/qa/fixtures/package_managers/maven/group/producer/settings.xml.erb index b670b83cf85..b670b83cf85 100644 --- a/qa/qa/fixtures/package_managers/maven/settings.xml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/producer/settings.xml.erb diff --git a/qa/qa/fixtures/package_managers/maven/settings_with_pat.xml.erb b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb index 611c232819f..611c232819f 100644 --- a/qa/qa/fixtures/package_managers/maven/settings_with_pat.xml.erb +++ b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb diff --git a/qa/qa/fixtures/package_managers/maven/project/gitlab_ci.yaml.erb b/qa/qa/fixtures/package_managers/maven/project/gitlab_ci.yaml.erb new file mode 100644 index 00000000000..44186e92ba7 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/project/gitlab_ci.yaml.erb @@ -0,0 +1,9 @@ +deploy-and-install: + image: maven:3.6-jdk-11 + script: + - 'mvn deploy -s settings.xml' + - 'mvn install -s settings.xml' + only: + - "<%= package_project.default_branch %>" + tags: + - "runner-for-<%= package_project.name %>"
\ No newline at end of file diff --git a/qa/qa/fixtures/package_managers/maven/project/pom.xml.erb b/qa/qa/fixtures/package_managers/maven/project/pom.xml.erb new file mode 100644 index 00000000000..5159172a170 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/project/pom.xml.erb @@ -0,0 +1,22 @@ + <project> + <groupId><%= group_id %></groupId> + <artifactId><%= artifact_id %></artifactId> + <version><%= package_version %></version> + <modelVersion>4.0.0</modelVersion> + <repositories> + <repository> + <id><%= package_project.name %></id> + <url><%= gitlab_address_with_port %>/api/v4/groups/<%= package_project.group.id %>/-/packages/maven</url> + </repository> + </repositories> + <distributionManagement> + <repository> + <id><%= package_project.name %></id> + <url><%= gitlab_address_with_port %>/api/v4/projects/<%= package_project.id %>/packages/maven</url> + </repository> + <snapshotRepository> + <id><%= package_project.name %></id> + <url><%= gitlab_address_with_port %>/api/v4/projects/<%= package_project.id %>/packages/maven</url> + </snapshotRepository> + </distributionManagement> +</project>
\ No newline at end of file diff --git a/qa/qa/fixtures/package_managers/maven/project/request_forwarding/gitlab_ci.yaml.erb b/qa/qa/fixtures/package_managers/maven/project/request_forwarding/gitlab_ci.yaml.erb new file mode 100644 index 00000000000..a41bdc4d650 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/project/request_forwarding/gitlab_ci.yaml.erb @@ -0,0 +1,8 @@ +install: + image: maven:3.6-jdk-11 + script: + - 'mvn install -U -s settings.xml' + only: + - "<%= imported_project.default_branch %>" + tags: + - "runner-for-<%= imported_project.name %>"
\ No newline at end of file diff --git a/qa/qa/fixtures/package_managers/maven/project/request_forwarding/settings.xml.erb b/qa/qa/fixtures/package_managers/maven/project/request_forwarding/settings.xml.erb new file mode 100644 index 00000000000..caf1fc9b761 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/project/request_forwarding/settings.xml.erb @@ -0,0 +1,23 @@ +<settings> + <servers> + <server> + <id>central-proxy</id> + <configuration> + <httpHeaders> + <property> + <name>Private-Token</name> + <value><%= personal_access_token %></value> + </property> + </httpHeaders> + </configuration> + </server> + </servers> + <mirrors> + <mirror> + <id>central-proxy</id> + <name>GitLab proxy of central repo</name> + <url><%= gitlab_address_with_port %>/api/v4/projects/<%= imported_project.id %>/packages/maven</url> + <mirrorOf>central</mirrorOf> + </mirror> + </mirrors> +</settings> diff --git a/qa/qa/fixtures/package_managers/maven/project/settings.xml.erb b/qa/qa/fixtures/package_managers/maven/project/settings.xml.erb new file mode 100644 index 00000000000..b670b83cf85 --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/project/settings.xml.erb @@ -0,0 +1,16 @@ +<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> +<servers> + <server> + <id><%= package_project.name %></id> + <configuration> + <httpHeaders> + <property> + <name><%= maven_header_name %></name> + <value><%= token %></value> + </property> + </httpHeaders> + </configuration> + </server> +</servers> +</settings>
\ No newline at end of file diff --git a/qa/qa/fixtures/software_licenses/other b/qa/qa/fixtures/software_licenses/other new file mode 100644 index 00000000000..b6652f05dab --- /dev/null +++ b/qa/qa/fixtures/software_licenses/other @@ -0,0 +1,7 @@ +This software is licensed under the Other license +======= + +This Other license isn't a real license and won't be used by any real project. +This license does not come with any guarantees. The author cannot be hold liable +in any way, and users are permitted to do anything they want with the provided +code.
\ No newline at end of file diff --git a/qa/qa/flow/purchase.rb b/qa/qa/flow/purchase.rb index c07e03c104d..32c4f469207 100644 --- a/qa/qa/flow/purchase.rb +++ b/qa/qa/flow/purchase.rb @@ -45,11 +45,12 @@ module QA Page::Group::Menu.perform(&:go_to_usage_quotas) Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quota| usage_quota.storage_tab - usage_quota.buy_storage + usage_quota.purchase_more_storage end - # Purchase checkout opens a new tab - Chemlab.configuration.browser.session.engine.switch_window + # Purchase checkout opens a new tab but buying additional storage does not + session = Chemlab.configuration.browser.session.engine + session.switch_window if session.windows.size == 2 Gitlab::Page::Subscriptions::New.perform do |storage| storage.quantity = quantity diff --git a/qa/qa/mobile/page/main/menu.rb b/qa/qa/mobile/page/main/menu.rb index 40bb421b383..73d3b9f7982 100644 --- a/qa/qa/mobile/page/main/menu.rb +++ b/qa/qa/mobile/page/main/menu.rb @@ -22,10 +22,10 @@ module QA end def open_mobile_menu - if has_no_element?(:user_avatar) + if has_no_element?(:user_avatar_content) Support::Retrier.retry_until do click_element(:mobile_navbar_button) - has_element?(:user_avatar) + has_element?(:user_avatar_content) end end end diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb index e55e156fb8a..3164676f8e4 100644 --- a/qa/qa/page/admin/menu.rb +++ b/qa/qa/page/admin/menu.rb @@ -5,78 +5,79 @@ module QA module Admin class Menu < Page::Base view 'app/views/layouts/nav/sidebar/_admin.html.haml' do - element :admin_sidebar - element :admin_sidebar_settings_submenu_content - element :admin_settings_item - element :admin_settings_repository_item - element :admin_settings_general_item - element :admin_settings_metrics_and_profiling_item + element :admin_sidebar_content + element :admin_monitoring_menu_link + element :admin_monitoring_submenu_content + element :admin_overview_submenu_content + element :admin_overview_users_link + element :admin_overview_groups_link + element :admin_settings_menu_link + element :admin_settings_submenu_content + element :admin_settings_general_link + element :admin_settings_integrations_link + element :admin_settings_metrics_and_profiling_link + element :admin_settings_network_link element :admin_settings_preferences_link - element :admin_monitoring_link - element :admin_sidebar_monitoring_submenu_content - element :admin_sidebar_overview_submenu_content - element :users_overview_link - element :groups_overview_link - element :integration_settings_link + element :admin_settings_repository_link end def go_to_preferences_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do click_element :admin_settings_preferences_link end end end def go_to_repository_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do - click_element :admin_settings_repository_item + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do + click_element :admin_settings_repository_link end end end def go_to_integration_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do - click_element :integration_settings_link + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do + click_element :admin_settings_integrations_link end end end def go_to_general_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do - click_element :admin_settings_general_item + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do + click_element :admin_settings_general_link end end end def go_to_metrics_and_profiling_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do - click_element :admin_settings_metrics_and_profiling_item + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do + click_element :admin_settings_metrics_and_profiling_link end end end def go_to_network_settings - hover_element(:admin_settings_item) do - within_submenu(:admin_sidebar_settings_submenu_content) do - click_element :admin_settings_network_item + hover_element(:admin_settings_menu_link) do + within_submenu(:admin_settings_submenu_content) do + click_element :admin_settings_network_link end end end def go_to_users_overview - within_submenu(:admin_sidebar_overview_submenu_content) do - click_element :users_overview_link + within_submenu(:admin_overview_submenu_content) do + click_element :admin_overview_users_link end end def go_to_groups_overview - within_submenu(:admin_sidebar_overview_submenu_content) do - click_element :groups_overview_link + within_submenu(:admin_overview_submenu_content) do + click_element :admin_overview_groups_link end end @@ -92,7 +93,7 @@ module QA end def within_sidebar - within_element(:admin_sidebar) do + within_element(:admin_sidebar_content) do yield end end diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 03f753b1d61..81c518bb4c6 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -242,6 +242,11 @@ module QA end def fill_element(name, content) + # `click_element_coordinates` is used to ensure the element is focused. + # Without it, flakiness can occur on pages with GitLab keyboard shortcuts enabled, + # where certain keys trigger actions when typed elsewhere on the page. + click_element_coordinates(name) + find_element(name).set(content) end diff --git a/qa/qa/page/component/clone_panel.rb b/qa/qa/page/component/clone_panel.rb index a0aea6fe44d..3ea29ff63da 100644 --- a/qa/qa/page/component/clone_panel.rb +++ b/qa/qa/page/component/clone_panel.rb @@ -11,18 +11,18 @@ module QA base.view 'app/views/projects/buttons/_clone.html.haml' do element :clone_dropdown - element :clone_options - element :ssh_clone_url - element :http_clone_url + element :clone_dropdown_content + element :ssh_clone_url_content + element :http_clone_url_content end end def repository_clone_http_location - repository_clone_location(:http_clone_url) + repository_clone_location(:http_clone_url_content) end def repository_clone_ssh_location - repository_clone_location(:ssh_clone_url) + repository_clone_location(:ssh_clone_url_content) end private @@ -31,7 +31,7 @@ module QA wait_until(reload: false) do click_element :clone_dropdown - within_element :clone_options do + within_element :clone_dropdown_content do Git::Location.new(find_element(kind).value) end end diff --git a/qa/qa/page/component/content_editor.rb b/qa/qa/page/component/content_editor.rb index f7b055b6052..e9fc575ae39 100644 --- a/qa/qa/page/component/content_editor.rb +++ b/qa/qa/page/component/content_editor.rb @@ -22,8 +22,8 @@ module QA element :file_upload_field end - base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do - element :wiki_hidden_content + base.view 'app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue' do + element :markdown_editor_form_field end end @@ -47,7 +47,7 @@ module QA end QA::Support::Retrier.retry_on_exception do - source = find_element(:wiki_hidden_content, visible: false) + source = find_element(:markdown_editor_form_field, visible: false) source.value =~ %r{uploads/.*#{::File.basename(image_path)}} end end diff --git a/qa/qa/page/component/groups_filter.rb b/qa/qa/page/component/groups_filter.rb index ff61c91f0f6..28b8ece918d 100644 --- a/qa/qa/page/component/groups_filter.rb +++ b/qa/qa/page/component/groups_filter.rb @@ -9,7 +9,7 @@ module QA def self.included(base) super - base.view 'app/views/shared/groups/_search_form.html.haml' do + base.view 'app/assets/javascripts/groups/components/overview_tabs.vue' do element :groups_filter_field end diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb index 5c39cfd3695..27dce152367 100644 --- a/qa/qa/page/component/invite_members_modal.rb +++ b/qa/qa/page/component/invite_members_modal.rb @@ -62,13 +62,9 @@ module QA Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) } - # Workaround for race condition with concurrent group API calls while searching - # Remove Retrier after https://gitlab.com/gitlab-org/gitlab/-/issues/349379 is resolved - Support::Retrier.retry_on_exception do - fill_element :group_select_dropdown_search_field, group_name - Support::WaitForRequests.wait_for_requests - click_button group_name - end + fill_element :group_select_dropdown_search_field, group_name + Support::WaitForRequests.wait_for_requests + click_button group_name set_access_level(access_level) end diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb index 68da89dc81d..71a69576c06 100644 --- a/qa/qa/page/component/issuable/sidebar.rb +++ b/qa/qa/page/component/issuable/sidebar.rb @@ -18,6 +18,10 @@ module QA element :more_assignees_link end + base.view 'app/assets/javascripts/sidebar/components/reviewers/reviewer_title.vue' do + element :reviewers_edit_button + end + base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue' do element :labels_block end @@ -37,6 +41,7 @@ module QA base.view 'app/views/shared/issuable/_sidebar.html.haml' do element :assignee_block_container element :milestone_block + element :reviewers_block_container end base.view 'app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue' do @@ -46,6 +51,10 @@ module QA base.view 'app/assets/javascripts/sidebar/components/sidebar_editable_item.vue' do element :edit_link end + + base.view 'app/helpers/dropdowns_helper.rb' do + element :dropdown_list_content + end end def assign_milestone(milestone) diff --git a/qa/qa/page/component/legacy_clone_panel.rb b/qa/qa/page/component/legacy_clone_panel.rb index f15d159a712..ee372a3f9aa 100644 --- a/qa/qa/page/component/legacy_clone_panel.rb +++ b/qa/qa/page/component/legacy_clone_panel.rb @@ -11,8 +11,8 @@ module QA base.view 'app/views/shared/_clone_panel.html.haml' do element :clone_dropdown - element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern - element :clone_url, 'text_field_tag :clone_url' # rubocop:disable QA/ElementWithPattern + element :clone_dropdown_content + element :clone_url_content end end @@ -28,7 +28,7 @@ module QA end def repository_location - Git::Location.new(find('#clone_url').value) + Git::Location.new(find_element(:clone_url_content).text) end private @@ -37,7 +37,7 @@ module QA wait_until(reload: false) do click_element :clone_dropdown - page.within('.clone-options-dropdown') do + within_element(:clone_dropdown_content) do click_link(kind) end diff --git a/qa/qa/page/component/namespace_select.rb b/qa/qa/page/component/namespace_select.rb index 4dbcb39ced6..9b483162f1b 100644 --- a/qa/qa/page/component/namespace_select.rb +++ b/qa/qa/page/component/namespace_select.rb @@ -9,7 +9,7 @@ module QA def self.included(base) super - base.view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue" do + base.view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select_deprecated.vue" do element :namespaces_list element :namespaces_list_groups element :namespaces_list_item @@ -20,8 +20,10 @@ module QA def select_namespace(item) click_element :namespaces_list + wait_for_requests + within_element(:namespaces_list) do - find_element(:namespaces_list_search).fill_in(with: item) + fill_element(:namespaces_list_search, item) wait_for_requests diff --git a/qa/qa/page/component/wiki_page_form.rb b/qa/qa/page/component/wiki_page_form.rb index 9e558844469..7a7329e6110 100644 --- a/qa/qa/page/component/wiki_page_form.rb +++ b/qa/qa/page/component/wiki_page_form.rb @@ -11,9 +11,12 @@ module QA base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do element :wiki_title_textbox - element :wiki_content_textarea element :wiki_message_textbox element :wiki_submit_button + end + + base.view 'app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue' do + element :markdown_editor_form_field element :editing_mode_button end @@ -27,7 +30,7 @@ module QA end def set_content(content) - fill_element(:wiki_content_textarea, content) + fill_element(:markdown_editor_form_field, content) end def set_message(message) diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb index b057a27fa3e..a30d489e6ff 100644 --- a/qa/qa/page/group/show.rb +++ b/qa/qa/page/group/show.rb @@ -13,10 +13,6 @@ module QA element :group_id_content end - view 'app/assets/javascripts/groups/constants.js' do - element :no_result_text, 'No groups or projects matched your search' # rubocop:disable QA/ElementWithPattern - end - view 'app/views/shared/members/_access_request_links.html.haml' do element :leave_group_link end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index aaf10e12e82..2f618224a73 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -14,7 +14,7 @@ module QA view 'app/views/layouts/header/_default.html.haml' do element :navbar, required: true element :canary_badge_link - element :user_avatar, required: !QA::Runtime::Env.mobile_layout? + element :user_avatar_content, required: !QA::Runtime::Env.mobile_layout? element :user_menu, required: !QA::Runtime::Env.mobile_layout? element :stop_impersonation_link element :issues_shortcut_button, required: !QA::Runtime::Env.mobile_layout? @@ -184,11 +184,11 @@ module QA end def has_personal_area?(wait: Capybara.default_max_wait_time) - has_element?(:user_avatar, wait: wait) + has_element?(:user_avatar_content, wait: wait) end def has_no_personal_area?(wait: Capybara.default_max_wait_time) - has_no_element?(:user_avatar, wait: wait) + has_no_element?(:user_avatar_content, wait: wait) end def has_admin_area_link?(wait: Capybara.default_max_wait_time) @@ -227,7 +227,7 @@ module QA def within_user_menu(&block) within_top_menu do - click_element :user_avatar unless has_element?(:user_profile_link, wait: 1) + click_element :user_avatar_content unless has_element?(:user_profile_link, wait: 1) within_element(:user_menu, &block) end diff --git a/qa/qa/page/main/terms.rb b/qa/qa/page/main/terms.rb index 024510c33cf..24f6b03549b 100644 --- a/qa/qa/page/main/terms.rb +++ b/qa/qa/page/main/terms.rb @@ -5,7 +5,7 @@ module QA module Main class Terms < Page::Base view 'app/views/layouts/terms.html.haml' do - element :user_avatar, required: true + element :user_avatar_content, required: true end view 'app/assets/javascripts/terms/components/app.vue' do diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb index 79eb4f2d51b..909b37943ff 100644 --- a/qa/qa/page/merge_request/new.rb +++ b/qa/qa/page/merge_request/new.rb @@ -38,7 +38,6 @@ module QA def click_diffs_tab click_element(:diffs_tab) - click_element(:dismiss_popover_button) if has_element?(:dismiss_popover_button, wait: 1) end def has_file?(file_name) diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 2587241ed18..e1add9ad434 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -108,6 +108,7 @@ module QA view 'app/assets/javascripts/vue_shared/components/markdown/header.vue' do element :suggestion_button + element :dismiss_suggestion_popover_button end view 'app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue' do @@ -191,8 +192,11 @@ module QA wait_until(sleep_interval: 5) do has_css?('a[data-linenumber="1"]') end + all_elements(:new_diff_line_link, minimum: 1).first.hover click_element(:diff_comment_button) + click_element(:dismiss_suggestion_popover_button) if has_element?(:dismiss_suggestion_popover_button, wait: 1) + fill_element(:reply_field, text) end @@ -208,7 +212,6 @@ module QA def click_diffs_tab click_element(:diffs_tab) - click_element(:dismiss_popover_button) if has_element?(:dismiss_popover_button, wait: 1) end def click_pipeline_link diff --git a/qa/qa/page/project/secure/configuration_form.rb b/qa/qa/page/project/secure/configuration_form.rb index fa1fad44273..20999f7c92a 100644 --- a/qa/qa/page/project/secure/configuration_form.rb +++ b/qa/qa/page/project/secure/configuration_form.rb @@ -9,6 +9,7 @@ module QA include QA::Page::Settings::Common view 'app/assets/javascripts/security_configuration/components/app.vue' do + element :security_configuration_container element :security_configuration_history_link end @@ -17,6 +18,7 @@ module QA element :sast_status, "`${feature.type}_status`" # rubocop:disable QA/ElementWithPattern element :sast_enable_button, "`${feature.type}_enable_button`" # rubocop:disable QA/ElementWithPattern element :dependency_scanning_mr_button, "`${feature.type}_mr_button`" # rubocop:disable QA/ElementWithPattern + element :license_scanning_status, "`${feature.type}_status`" # rubocop:disable QA/ElementWithPattern end view 'app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue' do @@ -67,6 +69,18 @@ module QA end end + def has_license_compliance_status?(status_text) + within_element(:license_scanning_status) do + has_text?(status_text) + end + end + + def has_no_license_compliance_status?(status_text) + within_element(:license_scanning_status) do + has_no_text?(status_text) + end + end + def has_auto_devops_container? has_element?(:autodevops_container) end @@ -80,6 +94,18 @@ module QA has_text?('Quickly enable all continuous testing and compliance tools by enabling Auto DevOps') end end + + def go_to_compliance_tab + go_to_tab('Compliance') + end + + private + + def go_to_tab(name) + within_element(:security_configuration_container) do + find('.nav-item', text: name).click + end + end end end end diff --git a/qa/qa/page/project/settings/default_branch.rb b/qa/qa/page/project/settings/default_branch.rb index cc28b37b88f..575f9006c84 100644 --- a/qa/qa/page/project/settings/default_branch.rb +++ b/qa/qa/page/project/settings/default_branch.rb @@ -5,16 +5,22 @@ module QA module Project module Settings class DefaultBranch < Page::Base - include Page::Component::Select2 - view 'app/views/projects/default_branch/_show.html.haml' do element :save_changes_button + end + + view 'app/assets/javascripts/projects/settings/components/default_branch_selector.vue' do element :default_branch_dropdown end + view 'app/assets/javascripts/ref/components/ref_selector.vue' do + element :ref_selector_searchbox + end + def set_default_branch(branch) - find('.select2-chosen').click - search_and_select(branch) + click_button :default_branch_dropdown + fill_in :ref_selector_searchbox, with: branch + click_button branch end def click_save_changes_button diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb index 7eeeeefdae6..f55faff19e7 100644 --- a/qa/qa/page/project/settings/mirroring_repositories.rb +++ b/qa/qa/page/project/settings/mirroring_repositories.rb @@ -62,7 +62,7 @@ module QA end def authentication_method=(value) - unless %w[Password None SSH\ public\ key].include?(value) + unless ['Password', 'None', 'SSH public key'].include?(value) raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'" end diff --git a/qa/qa/page/project/settings/services/jira.rb b/qa/qa/page/project/settings/services/jira.rb index 827508e488c..41034bbd897 100644 --- a/qa/qa/page/project/settings/services/jira.rb +++ b/qa/qa/page/project/settings/services/jira.rb @@ -41,10 +41,7 @@ module QA yield self if block_given? - click_save_changes_button - wait_until(reload: false) do - has_element?(:save_changes_button, wait: 1) ? !find_element(:save_changes_button).disabled? : true - end + click_save_changes_and_wait end def enable_jira_issues @@ -55,6 +52,13 @@ module QA fill_element(:service_jira_project_key_field, key) end + def click_save_changes_and_wait + click_save_changes_button + wait_until(reload: false) do + has_element?(:save_changes_button, wait: 1) ? !find_element(:save_changes_button).disabled? : true + end + end + private def set_jira_server_url(url) @@ -94,3 +98,5 @@ module QA end end end + +QA::Page::Project::Settings::Services::Jira.prepend_mod_with('Page::Project::Settings::Services::Jira', namespace: QA) diff --git a/qa/qa/page/project/tag/new.rb b/qa/qa/page/project/tag/new.rb index dc59c07ec98..50e11acd94a 100644 --- a/qa/qa/page/project/tag/new.rb +++ b/qa/qa/page/project/tag/new.rb @@ -8,17 +8,9 @@ module QA view 'app/views/projects/tags/new.html.haml' do element :tag_name_field element :tag_message_field - element :release_notes_field element :create_tag_button end - view 'app/views/shared/_zen.html.haml' do - # This partial adds the `release_notes_field` selector passed from 'app/views/projects/tags/new.html.haml' - # The checks below ensure that required lines are not removed without updating this page object - element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern - element :_, "text_area_tag attr, current_text, data: { qa_selector: qa_selector }" # rubocop:disable QA/ElementWithPattern - end - def fill_tag_name(text) fill_element(:tag_name_field, text) end @@ -27,10 +19,6 @@ module QA fill_element(:tag_message_field, text) end - def fill_release_notes(text) - fill_element(:release_notes_field, text) - end - def click_create_tag_button click_element :create_tag_button end diff --git a/qa/qa/resource/issuable.rb b/qa/qa/resource/issuable.rb new file mode 100644 index 00000000000..6ebdaac8298 --- /dev/null +++ b/qa/qa/resource/issuable.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module QA + module Resource + class Issuable < Base + # Commentes (notes) path + # + # @return [String] + def api_comments_path + "#{api_get_path}/notes" + end + + # Get issue comments + # + # @return [Array] + def comments(auto_paginate: false, attempts: 0) + return parse_body(api_get_from(api_comments_path)) unless auto_paginate + + auto_paginated_response( + Runtime::API::Request.new(api_client, api_comments_path, per_page: '100').url, + attempts: attempts + ) + end + + # Create a new comment + # + # @param [String] body + # @param [Boolean] confidential + # @return [Hash] + def add_comment(body:, confidential: false) + api_post_to(api_comments_path, body: body, confidential: confidential) + end + + # Issue label events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def label_events(auto_paginate: false, attempts: 0) + events("label", auto_paginate: auto_paginate, attempts: attempts) + end + + # Issue state events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def state_events(auto_paginate: false, attempts: 0) + events("state", auto_paginate: auto_paginate, attempts: attempts) + end + + # Issue milestone events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def milestone_events(auto_paginate: false, attempts: 0) + events("milestone", auto_paginate: auto_paginate, attempts: attempts) + end + + private + + # Issue events + # + # @param [String] name event name + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def events(name, auto_paginate:, attempts:) + return parse_body(api_get_from("#{api_get_path}/resource_#{name}_events")) unless auto_paginate + + auto_paginated_response( + Runtime::API::Request.new(api_client, "#{api_get_path}/resource_#{name}_events", per_page: '100').url, + attempts: attempts + ) + end + end + end +end diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb index 1e38de97c1e..2e18e1d0323 100644 --- a/qa/qa/resource/issue.rb +++ b/qa/qa/resource/issue.rb @@ -2,7 +2,7 @@ module QA module Resource - class Issue < Base + class Issue < Issuable attr_writer :milestone, :template, :weight attribute :project do @@ -53,10 +53,6 @@ module QA "/projects/#{project.id}/issues/#{iid}" end - def api_comments_path - "#{api_get_path}/notes" - end - def api_post_body { assignee_ids: assignee_ids, @@ -83,27 +79,6 @@ module QA QA::Runtime::Logger.debug("Successfully updated issue assignees to #{assignee_ids}") end - # Get issue comments - # - # @return [Array] - def comments(auto_paginate: false, attempts: 0) - return parse_body(api_get_from(api_comments_path)) unless auto_paginate - - auto_paginated_response( - Runtime::API::Request.new(api_client, api_comments_path, per_page: '100').url, - attempts: attempts - ) - end - - # Create a new comment - # - # @param [String] body - # @param [Boolean] confidential - # @return [Hash] - def add_comment(body:, confidential: false) - api_post_to(api_comments_path, body: body, confidential: confidential) - end - protected # Return subset of fields for comparing issues diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb index 0a92553690f..5d6dc12ac9c 100644 --- a/qa/qa/resource/merge_request.rb +++ b/qa/qa/resource/merge_request.rb @@ -2,7 +2,7 @@ module QA module Resource - class MergeRequest < Base + class MergeRequest < Issuable attr_accessor :approval_rules, :source_branch, :target_new_branch, @@ -130,10 +130,6 @@ module QA } end - def api_comments_path - "#{api_get_path}/notes" - end - def merge_via_api! Support::Waiter.wait_until(sleep_interval: 1) do QA::Runtime::Logger.debug("Waiting until merge request with id '#{iid}' can be merged") @@ -166,26 +162,6 @@ module QA current_url end - # Get MR comments - # - # @return [Array] - def comments(auto_paginate: false, attempts: 0) - return parse_body(api_get_from(api_comments_path)) unless auto_paginate - - auto_paginated_response( - Runtime::API::Request.new(api_client, api_comments_path, per_page: '100').url, - attempts: attempts - ) - end - - # Add mr comment - # - # @param [String] body - # @return [Hash] - def add_comment(body) - api_post_to(api_comments_path, body: body) - end - # Return subset of fields for comparing merge requests # # @return [Hash] diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index 13c6f285259..e5df95f1fa5 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -226,7 +226,11 @@ module QA end def api_housekeeping_path - "/projects/#{id}/housekeeping" + "#{api_get_path}/housekeeping" + end + + def api_protected_branches_path + "#{api_get_path}/protected_branches" end def api_post_body @@ -324,8 +328,11 @@ module QA result = parse_body(response) if result[:import_status] == "failed" - Runtime::Logger.error("Import failed: #{result[:import_error]}") - Runtime::Logger.error("Failed relations: #{result[:failed_relations]}") + Runtime::Logger.error(<<~ERR) + Import of project '#{full_path}' failed! + error: '#{result[:import_error]}' + failed relations: '#{result[:failed_relations]}' + ERR end result @@ -420,7 +427,7 @@ module QA end def wikis - response = get(request_url(api_wikis_path)) + response = api_get_from(api_wikis_path) parse_body(response) end @@ -437,6 +444,11 @@ module QA api_post_to(api_releases_path, tag_name: tag, ref: ref, **params) end + def protected_branches + response = api_get_from(api_protected_branches_path) + parse_body(response) + end + # Uses the API to wait until a pull mirroring update is successful (pull mirroring is treated as an import) def wait_for_pull_mirroring mirror_succeeded = Support::Retrier.retry_until( diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb index b9dbd2a6131..9ba9723f0cc 100644 --- a/qa/qa/resource/project_imported_from_github.rb +++ b/qa/qa/resource/project_imported_from_github.rb @@ -3,6 +3,8 @@ module QA module Resource class ProjectImportedFromGithub < Resource::Project + attr_accessor :issue_events_import, :full_notes_import, :attachments_import + attribute :github_repo_id do github_client.repository(github_repository_path).id end @@ -51,7 +53,12 @@ module QA new_name: name, target_namespace: @personal_namespace || group.full_path, personal_access_token: github_personal_access_token, - ci_cd_only: false + ci_cd_only: false, + optional_stages: { + single_endpoint_issue_events_import: issue_events_import, + single_endpoint_notes_import: full_notes_import, + attachments_import: attachments_import + } } end diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb index 55ad6edb3c1..7b6a3d296c4 100644 --- a/qa/qa/resource/protected_branch.rb +++ b/qa/qa/resource/protected_branch.rb @@ -22,9 +22,7 @@ module QA commit.branch = branch_name commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: "new_file-#{SecureRandom.hex(8)}.md", content: 'new file' } - ]) + commit.add_files([{ file_path: "new_file-#{SecureRandom.hex(8)}.md", content: 'new file' }]) end end diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb index a974446b3cb..71a5e1c8930 100644 --- a/qa/qa/resource/user.rb +++ b/qa/qa/resource/user.rb @@ -44,7 +44,7 @@ module QA alias_method :ldap_username, :username def password - @password ||= SecureRandom.hex(8) + @password ||= "Pa$$w0rd" end alias_method :ldap_password, :password diff --git a/qa/qa/resource/wiki/group_page.rb b/qa/qa/resource/wiki/group_page.rb index 1e40426a389..69ad83ea10a 100644 --- a/qa/qa/resource/wiki/group_page.rb +++ b/qa/qa/resource/wiki/group_page.rb @@ -23,6 +23,7 @@ module QA end def initialize + # Note: A Group Wiki Home page requires title = 'Home', otherwise when going /-/wikis, Rails will render a new page creation form. @title = 'Home' @content = 'This wiki page is created via API' end diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index b1a912ac43e..782ba1cf2fa 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -29,6 +29,15 @@ module QA @gitlab_url ||= ENV["QA_GITLAB_URL"] || "http://127.0.0.1:3000" # default to GDK end + # Retrieves the value of the gitlab_canary cookie if set or returns an empty hash. + # + # @return [Hash] + def canary_cookie + canary = ENV['QA_COOKIES']&.scan(/gitlab_canary=(true|false)/)&.dig(0, 0) + + canary ? { gitlab_canary: canary } : {} + end + def additional_repository_storage ENV['QA_ADDITIONAL_REPOSITORY_STORAGE'] end diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb deleted file mode 100644 index b4098619e4e..00000000000 --- a/qa/qa/scenario/test/instance.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module QA - module Scenario - module Test - # This class exists for back-compatibility so that gitlab-qa can continue - # to call Test::Instance instead of Test::Instance::All until at least - # the current latest GitLab version has the Test::Instance::All class. - # As of Aug, 22nd 2018. Only GitLab >= 11.3 has this class. - module Instance - include Bootable - - def self.perform(*args) - self.tap do |scenario| - yield scenario if block_given? - break scenario.do_perform(*args) - end - end - - def self.do_perform(address, *rspec_options) - Runtime::Scenario.define(:gitlab_address, address) - - ## - # Perform before hooks, which are different for CE and EE - # - Runtime::Release.perform_before_hooks - - Specs::Runner.perform do |specs| - specs.tty = true - specs.options = rspec_options if rspec_options.any? - end - end - end - end - end -end diff --git a/qa/qa/scenario/test/integration/github.rb b/qa/qa/scenario/test/integration/github.rb deleted file mode 100644 index 857a1f00bd5..00000000000 --- a/qa/qa/scenario/test/integration/github.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module QA - module Scenario - module Test - module Integration - class Github < Test::Instance::All - tags :github - - def perform(address, *rspec_options) - # This test suite requires a GitHub personal access token - Runtime::Env.require_github_access_token! - - super - end - end - end - end - end -end diff --git a/qa/qa/specs/features/api/1_manage/group_access_token_spec.rb b/qa/qa/specs/features/api/1_manage/group_access_token_spec.rb index 7d3916641aa..bf95a215c8e 100644 --- a/qa/qa/specs/features/api/1_manage/group_access_token_spec.rb +++ b/qa/qa/specs/features/api/1_manage/group_access_token_spec.rb @@ -40,9 +40,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' } - ]) + commit.add_files([{ file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' }]) end end.not_to raise_error end diff --git a/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb index df34bf32421..c3e41e9298b 100644 --- a/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb @@ -3,7 +3,7 @@ module QA # Spec uses real github.com, which means outage of github.com can actually block deployment # Keep spec in reliable bucket but don't run in blocking pipelines - RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin do + RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do describe 'Project import', issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/353583' do let!(:api_client) { Runtime::API::Client.as_admin } let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } } @@ -20,7 +20,9 @@ module QA project.group = group project.github_personal_access_token = Runtime::Env.github_access_token project.github_repository_path = 'gitlab-qa-github/import-test' - project.api_client = api_client + project.api_client = Runtime::API::Client.new(user: user) + project.issue_events_import = true + project.full_notes_import = true end end @@ -41,6 +43,7 @@ module QA aggregate_failures do verify_status_data verify_repository_import + verify_protected_branches_import verify_commits_import verify_labels_import verify_issues_import @@ -53,7 +56,7 @@ module QA def verify_status_data stats = imported_project.project_import_status.dig(:stats, :imported) expect(stats).to include( - # issue: 2, + issue: 1, label: 9, milestone: 1, note: 3, @@ -69,6 +72,21 @@ module QA expect(imported_project.api_response[:import_error]).to be_nil end + def verify_protected_branches_import + branches = imported_project.protected_branches.map do |branch| + branch.slice(:name, :allow_force_push, :code_owner_approval_required) + end + expect(branches.first).to include( + { + name: 'main' + # TODO: Add validation once https://gitlab.com/groups/gitlab-org/-/epics/8585 is closed + # At the moment both options are always set to false regardless of state in github + # allow_force_push: true, + # code_owner_approval_required: true + } + ) + end + def verify_commits_import expect(imported_project.commits.length).to eq(2) end @@ -122,7 +140,7 @@ module QA mr.iid = merge_requests.first[:iid] mr.api_client = api_client end.reload! - mr_comments = merge_request.comments.map { |comment| comment[:body] } # rubocop:disable Rails/Pluck + mr_comments = merge_request.comments.map { |comment| comment[:body] } expect(merge_requests.length).to eq(1) expect(merge_request.api_resource).to include( diff --git a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb index e6b60a5b090..5acf15dd2b4 100644 --- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb @@ -1,21 +1,82 @@ # frozen_string_literal: true +require "etc" + # Lifesize project import test executed from https://gitlab.com/gitlab-org/manage/import/import-metrics # rubocop:disable Rails/Pluck module QA RSpec.describe 'Manage', :github, requires_admin: 'creates users', only: { job: 'large-github-import' } do - describe 'Project import' do + describe 'Project import', product_group: :import do # rubocop:disable RSpec/MultipleMemoizedHelpers + let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' } + let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION']&.to_i || 7200 } let(:logger) { Runtime::Logger.logger } let(:differ) { RSpec::Support::Differ.new(color: true) } - let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address } + let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address.chomp("/") } let(:dummy_url) { "https://example.com" } + let(:api_request_params) { { auto_paginate: true, attempts: 2 } } let(:created_by_pattern) { /\*Created by: \S+\*\n\n/ } let(:suggestion_pattern) { /suggestion:-\d+\+\d+/ } let(:gh_link_pattern) { %r{https://github.com/#{github_repo}/(issues|pull)} } let(:gl_link_pattern) { %r{#{gitlab_address}/#{imported_project.path_with_namespace}/-/(issues|merge_requests)} } - let(:event_pattern) { %r{(un)?assigned( to)? @\S+|mentioned in (issue|merge request) [!#]\d+|changed title from \*\*.*\*\* to \*\*.*\*\*} } # rubocop:disable Layout/LineLength + # rubocop:disable Lint/MixedRegexpCaptureTypes + let(:event_pattern) do + Regexp.union( + [ + /(?<event>(un)?assigned)( to)? @\S+/, + /(?<event>mentioned) in (issue|merge request) [!#]\d+/, + /(?<event>changed title) from \*\*.*\*\* to \*\*.*\*\*/, + /(?<event>requested review) from @\w+/, + /\*(?<event>Merged) by:/, + /\*\*(Review):\*\*/ + ] + ) + end + # rubocop:enable Lint/MixedRegexpCaptureTypes + + # mapping from gitlab to github names + let(:event_mapping) do + { + "label_add" => "labeled", + "label_remove" => "unlabeled", + "milestone_add" => "milestoned", + "milestone_remove" => "demilestoned", + "assigned" => "assigned", + "unassigned" => "unassigned", + "changed title" => "renamed", + "requested review" => "review_requested", + "Merged" => "merged" + } + end + + # github events that are not migrated or are not correctly mapable in gitlab + let(:unsupported_events) do + [ + "head_ref_deleted", + "head_ref_force_pushed", + "head_ref_restored", + "base_ref_force_pushed", + "base_ref_changed", + "review_request_removed", + "review_dismissed", + "auto_squash_enabled", + "auto_merge_disabled", + "comment_deleted", + "convert_to_draft", + "ready_for_review", + "subscribed", + "unsubscribed", + "transferred", + "locked", + "unlocked", + # mentions are supported but they can be reported differently on gitlab's side + # for example mention of issue creation in pr will be reported in the issue on gitlab side + # or referenced in github will still create a 'mentioned in' comment in gitlab + "referenced", + "mentioned" + ] + end let(:api_client) { Runtime::API::Client.as_admin } @@ -25,79 +86,105 @@ module QA end end - let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' } - let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION'] ? ENV['QA_LARGE_IMPORT_DURATION'].to_i : 7200 } let(:github_client) do Octokit::Client.new( access_token: ENV['QA_LARGE_IMPORT_GH_TOKEN'] || Runtime::Env.github_access_token, - auto_paginate: true + auto_paginate: true, + middleware: Faraday::RackBuilder.new do |builder| + builder.use(Faraday::Retry::Middleware, exceptions: [Octokit::InternalServerError, Octokit::ServerError]) + end ) end let(:gh_repo) { github_client.repository(github_repo) } let(:gh_branches) do - logger.debug("= Fetching branches =") + logger.info("= Fetching branches =") github_client.branches(github_repo).map(&:name) end let(:gh_commits) do - logger.debug("= Fetching commits =") + logger.info("= Fetching commits =") github_client.commits(github_repo).map(&:sha) end let(:gh_labels) do - logger.debug("= Fetching labels =") + logger.info("= Fetching labels =") github_client.labels(github_repo).map { |label| { name: label.name, color: "##{label.color}" } } end let(:gh_milestones) do - logger.debug("= Fetching milestones =") + logger.info("= Fetching milestones =") github_client .list_milestones(github_repo, state: 'all') .map { |ms| { title: ms.title, description: ms.description } } end - let(:gh_all_issues) do - logger.debug("= Fetching issues and prs =") - github_client.list_issues(github_repo, state: 'all') - end - let(:gh_prs) do gh_all_issues.select(&:pull_request).each_with_object({}) do |pr, hash| - hash[pr.number] = { + id = pr.number + hash[id] = { url: pr.html_url, title: pr.title, body: pr.body || '', - comments: [*gh_pr_comments[pr.html_url], *gh_issue_comments[pr.html_url]].compact + comments: [*gh_pr_comments[id], *gh_issue_comments[id]].compact, + events: gh_pr_events[id].reject { |event| unsupported_events.include?(event) } } end end let(:gh_issues) do gh_all_issues.reject(&:pull_request).each_with_object({}) do |issue, hash| - hash[issue.number] = { + id = issue.number + hash[id] = { url: issue.html_url, title: issue.title, body: issue.body || '', - comments: gh_issue_comments[issue.html_url] + comments: gh_issue_comments[id], + events: gh_issue_events[id].reject { |event| unsupported_events.include?(event) } } end end + let(:gh_all_issues) do + logger.info("= Fetching issues and prs =") + github_client.list_issues(github_repo, state: 'all') + end + + let(:gh_all_events) do + logger.info("- Fetching issue and pr events -") + github_client.repository_issue_events(github_repo).map do |event| + { name: event[:event], **(event[:issue] || {}) } # some events don't have issue object at all + end + end + + let(:gh_issue_events) do + gh_all_events.each_with_object(Hash.new { |h, k| h[k] = [] }) do |event, hash| + next if event[:pull_request] || !event[:number] + + hash[event[:number]] << event[:name] + end + end + + let(:gh_pr_events) do + gh_all_events.each_with_object(Hash.new { |h, k| h[k] = [] }) do |event, hash| + next unless event[:pull_request] + + hash[event[:number]] << event[:name] + end + end + let(:gh_issue_comments) do - logger.debug("= Fetching issue comments =") + logger.info("- Fetching issue comments -") github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash| - # use base html url as key - hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) + hash[id_from_url(c.html_url)] << c.body&.gsub(gh_link_pattern, dummy_url) end end let(:gh_pr_comments) do - logger.debug("= Fetching pr comments =") + logger.info("- Fetching pr comments -") github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash| - # use base html url as key - hash[c.html_url.gsub(/\#\S+/, "")] << c.body + hash[id_from_url(c.html_url)] << c.body # some suggestions can contain extra whitespaces which gitlab will remove &.gsub(/suggestion\s+\r/, "suggestion\r") &.gsub(gh_link_pattern, dummy_url) @@ -111,11 +198,12 @@ module QA project.github_personal_access_token = Runtime::Env.github_access_token project.github_repository_path = github_repo project.personal_namespace = user.username - project.api_client = api_client + project.api_client = Runtime::API::Client.new(user: user) + project.issue_events_import = true + project.full_notes_import = true end end - # rubocop:disable RSpec/InstanceVariable after do |example| next unless defined?(@import_time) @@ -138,8 +226,10 @@ module QA milestones: gh_milestones.length, mrs: gh_prs.length, mr_comments: gh_prs.sum { |_k, v| v[:comments].length }, + mr_events: gh_prs.sum { |_k, v| v[:events].length }, issues: gh_issues.length, - issue_comments: gh_issues.sum { |_k, v| v[:comments].length } + issue_comments: gh_issues.sum { |_k, v| v[:comments].length }, + issue_events: gh_issues.sum { |_k, v| v[:events].length } } }, target: { @@ -153,8 +243,10 @@ module QA milestones: gl_milestones.length, mrs: mrs.length, mr_comments: mrs.sum { |_k, v| v[:comments].length }, + mr_events: mrs.sum { |_k, v| v[:events].length }, issues: gl_issues.length, - issue_comments: gl_issues.sum { |_k, v| v[:comments].length } + issue_comments: gl_issues.sum { |_k, v| v[:comments].length }, + issue_events: gl_issues.sum { |_k, v| v[:events].length } } }, not_imported: { @@ -164,7 +256,6 @@ module QA } ) end - # rubocop:enable RSpec/InstanceVariable it( 'imports large Github repo via api', @@ -172,8 +263,9 @@ module QA ) do start = Time.now - # import the project and log gitlab path - logger.info("== Importing project '#{github_repo}' in to '#{imported_project.reload!.full_path}' ==") + # trigger import and log project paths + logger.info("== Triggering import of project '#{github_repo}' in to '#{imported_project.reload!.full_path}' ==") + # fetch all objects right after import has started fetch_github_objects @@ -182,7 +274,7 @@ module QA @stats = status.dig(:stats, :imported) # fail fast if import explicitly failed - raise "Import of '#{imported_project.name}' failed!" if status[:import_status] == 'failed' + raise "Import of '#{imported_project.full_path}' failed!" if status[:import_status] == 'failed' status[:import_status] end @@ -276,25 +368,26 @@ module QA count_msg = "Expected to contain same amount of #{type}s. Gitlab: #{expected.length}, Github: #{actual.length}" expect(expected.length).to eq(actual.length), count_msg - missing_comments = verify_comments(type, actual, expected) + missing_objects = (actual.keys - expected.keys).map { |it| actual[it].slice(:title, :url) } + missing_content = verify_comments_and_events(type, actual, expected) { - "#{type}s": (actual.keys - expected.keys).map { |it| actual[it].slice(:title, :url) }, - "#{type}_comments": missing_comments - } + "#{type}s": missing_objects.empty? ? nil : missing_objects, + "#{type}_content": missing_content.empty? ? nil : missing_content + }.compact end - # Verify imported comments + # Verify imported comments and events # # @param [String] type verification object, 'mrs' or 'issues' # @param [Hash] actual # @param [Hash] expected # @return [Hash] - def verify_comments(type, actual, expected) - actual.each_with_object([]) do |(key, actual_item), missing_comments| + def verify_comments_and_events(type, actual, expected) + actual.each_with_object([]) do |(key, actual_item), missing_content| expected_item = expected[key] title = actual_item[:title] - msg = "expected #{type} with title '#{title}' to have" + msg = "expected #{type} with iid '#{key}' to have" # Print title in the error message to see which object is missing # @@ -320,17 +413,27 @@ module QA expect(expected_comments.length).to eq(actual_comments.length), comment_count_msg expect(expected_comments).to match_array(actual_comments) - # Save missing comments + expected_events = expected_item[:events] + actual_events = actual_item[:events] + event_count_msg = <<~MSG + #{msg} same amount of events. Gitlab: #{expected_events.length}, Github: #{actual_events.length} + MSG + expect(expected_events.length).to eq(actual_events.length), event_count_msg + expect(expected_events).to match_array(actual_events) + + # Save missing comments and events # comment_diff = actual_comments - expected_comments - next if comment_diff.empty? + event_diff = actual_events - expected_events + next if comment_diff.empty? && event_diff.empty? - missing_comments << { + missing_content << { title: title, github_url: actual_item[:url], gitlab_url: expected_item[:url], - missing_comments: comment_diff - } + missing_comments: comment_diff.empty? ? nil : comment_diff, + missing_events: event_diff.empty? ? nil : event_diff + }.compact end end @@ -380,26 +483,27 @@ module QA def mrs @mrs ||= begin logger.debug("= Fetching merge requests =") - imported_mrs = imported_project.merge_requests(auto_paginate: true, attempts: 2) + imported_mrs = imported_project.merge_requests(**api_request_params) logger.debug("= Fetching merge request comments =") - Parallel.map(imported_mrs, in_threads: 4) do |mr| + Parallel.map(imported_mrs, in_threads: Etc.nprocessors) do |mr| resource = Resource::MergeRequest.init do |resource| resource.project = imported_project resource.iid = mr[:iid] resource.api_client = api_client end - logger.debug("Fetching comments for mr '#{mr[:title]}'") - comments = resource - .comments(auto_paginate: true, attempts: 2) - .reject { |c| c[:system] || c[:body].match?(/^(\*\*Review:\*\*)|(\*Merged by:).*/) } + logger.debug("Fetching events and comments for mr '!#{mr[:iid]}'") + comments = resource.comments(**api_request_params) + label_events = resource.label_events(**api_request_params) + state_events = resource.state_events(**api_request_params) + milestone_events = resource.milestone_events(**api_request_params) [mr[:iid], { url: mr[:web_url], title: mr[:title], body: sanitize_description(mr[:description]) || '', - events: events(comments), + events: events(comments, label_events, state_events, milestone_events), comments: non_event_comments(comments) }] end.to_h @@ -412,48 +516,59 @@ module QA def gl_issues @gl_issues ||= begin logger.debug("= Fetching issues =") - imported_issues = imported_project.issues(auto_paginate: true, attempts: 2) + imported_issues = imported_project.issues(**api_request_params) logger.debug("= Fetching issue comments =") - Parallel.map(imported_issues, in_threads: 4) do |issue| + Parallel.map(imported_issues, in_threads: Etc.nprocessors) do |issue| resource = Resource::Issue.init do |issue_resource| issue_resource.project = imported_project issue_resource.iid = issue[:iid] issue_resource.api_client = api_client end - logger.debug("Fetching comments for issue '#{issue[:title]}'") - comments = resource.comments(auto_paginate: true, attempts: 2) + logger.debug("Fetching events and comments for issue '!#{issue[:iid]}'") + comments = resource.comments(**api_request_params) + label_events = resource.label_events(**api_request_params) + state_events = resource.state_events(**api_request_params) + milestone_events = resource.milestone_events(**api_request_params) [issue[:iid], { url: issue[:web_url], title: issue[:title], body: sanitize_description(issue[:description]) || '', - events: events(comments), + events: events(comments, label_events, state_events, milestone_events), comments: non_event_comments(comments) }] end.to_h end end - # Fetch comments without events + # Filter out event comments # # @param [Array] comments # @return [Array] def non_event_comments(comments) comments - .reject { |c| c[:body].match?(event_pattern) } + .reject { |c| c[:system] || c[:body].match?(event_pattern) } .map { |c| sanitize_comment(c[:body]) } end # Events # # @param [Array] comments + # @param [Array] label_events + # @param [Array] state_events + # @param [Array] milestone_events # @return [Array] - def events(comments) - comments - .select { |c| c[:body].match?(event_pattern) } - .map { |c| c[:body] } + def events(comments, label_events, state_events, milestone_events) + mapped_label_events = label_events.map { |event| event_mapping["label_#{event[:action]}"] } + mapped_milestone_events = milestone_events.map { |event| event_mapping["milestone_#{event[:action]}"] } + mapped_state_event = state_events.map { |event| event[:state] } + mapped_comment_events = comments.map do |c| + event_mapping[c[:body].match(event_pattern)&.named_captures&.fetch("event", nil)] + end + + [*mapped_label_events, *mapped_milestone_events, *mapped_state_event, *mapped_comment_events].compact end # Normalize comments and make them directly comparable @@ -489,6 +604,16 @@ module QA def save_json(name, json) File.open("tmp/#{name}.json", "w") { |file| file.write(JSON.pretty_generate(json)) } end + + # Extract id number from web url of issue or pull request + # + # Some endpoints don't return object id as separate parameter so web url can be used as a workaround + # + # @param [String] url + # @return [Integer] + def id_from_url(url) + url.match(%r{(?<type>issues|pull)/(?<id>\d+)})&.named_captures&.fetch("id", nil).to_i + end end end end diff --git a/qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb index aae0329003b..a2d66ffe8d3 100644 --- a/qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb +++ b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do - describe 'WebHooks integration', :requires_admin, :integrations, :orchestrated do + RSpec.describe 'Manage' do + describe 'WebHooks integration', :requires_admin, :integrations, :orchestrated, product_group: :integrations do before(:context) do toggle_local_requests(true) end @@ -70,7 +70,8 @@ module QA end end - it 'sends an issues and note event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349723' do + it 'sends an issues and note event', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349723' do setup_webhook(issues: true, note: true) do |webhook, smocker| issue = Resource::Issue.fabricate_via_api! do |issue_init| issue_init.project = webhook.project diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb index f721b3326a0..e17e12cdaf3 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage', :reliable, :requires_admin do + RSpec.describe 'Manage', :reliable, :requires_admin, product_group: :import do describe 'Gitlab migration' do let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } } let(:admin_api_client) { Runtime::API::Client.as_admin } diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb index 74a00e1c74c..c1f11b15068 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' let!(:source_issue) do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb index 83cc44f9958..5689fa169ce 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb @@ -5,7 +5,7 @@ # rubocop:disable Rails/Pluck, Layout/LineLength, RSpec/MultipleMemoizedHelpers module QA RSpec.describe "Manage", requires_admin: 'creates users', only: { job: 'large-gitlab-import' } do - describe "Gitlab migration" do + describe "Gitlab migration", product_group: :import do let(:logger) { Runtime::Logger.logger } let(:differ) { RSpec::Support::Differ.new(color: true) } let(:gitlab_group) { ENV['QA_LARGE_IMPORT_GROUP'] || 'gitlab-migration' } diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb index 704325d9235..aa4d3becbe7 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' let(:member) do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb index d656ea4dea5..92cba005832 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' context 'with merge request' do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb index 484c32956e3..3db4ff4351e 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' context 'with ci pipeline' do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb index 421dbe56a99..3e0df3d1e13 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' context 'with uninitialized project' do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb index 6910b6a7fa2..91dcfe6a1a3 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb @@ -4,7 +4,7 @@ require_relative 'gitlab_project_migration_common' module QA RSpec.describe 'Manage' do - describe 'Gitlab migration' do + describe 'Gitlab migration', product_group: :import do include_context 'with gitlab project migration' context 'with release' do diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb index 3581ad3d207..9c80c088917 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true module QA - # Disable on live envs until bulk_import_projects toggle is on by default - # Otherwise tests running in parallel can disable feature in the middle of other test RSpec.shared_context 'with gitlab project migration', requires_admin: 'creates a user via API', + quarantine: { + type: :flaky, + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/364839' + }, feature_flag: { name: 'bulk_import_projects', scope: :global diff --git a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb index 539da92f471..c4be90d3759 100644 --- a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb +++ b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb @@ -33,9 +33,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = @project_access_token.project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' } - ]) + commit.add_files([{ file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' }]) end end.not_to raise_error end @@ -67,9 +65,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = @different_project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' } - ]) + commit.add_files([{ file_path: "text-#{SecureRandom.hex(8)}.txt", content: 'new file' }]) end end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden - You are not allowed to push into this branch/) end diff --git a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb index 874626e01f1..24088057abc 100644 --- a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb +++ b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do - describe 'rate limits', :reliable do + describe 'rate limits', :reliable, product_group: :integrations do let(:rate_limited_user) { Resource::User.fabricate_via_api! } let(:api_client) { Runtime::API::Client.new(:gitlab, user: rate_limited_user) } let!(:request) { Runtime::API::Request.new(api_client, '/users') } diff --git a/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb b/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb index e518bbfc6f7..28c20344b29 100644 --- a/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb +++ b/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb @@ -69,9 +69,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = @project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: 'test.txt', content: 'new file' } - ]) + commit.add_files([{ file_path: 'test.txt', content: 'new file' }]) end end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden - You are not allowed to push into this branch/) end diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb index 9f0e2664213..16d4fd35b69 100644 --- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb +++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'User', :requires_admin do + describe 'User', :requires_admin, product_group: :workspace do let(:admin_api_client) { Runtime::API::Client.as_admin } let!(:sub_group) do @@ -85,9 +85,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = sub_group_project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: 'test.txt', content: 'new file' } - ]) + commit.add_files([{ file_path: 'test.txt', content: 'new file' }]) end end.not_to raise_error end @@ -167,9 +165,7 @@ module QA commit.branch = "new_branch_#{SecureRandom.hex(8)}" commit.start_branch = parent_group_project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: 'test.txt', content: 'new file' } - ]) + commit.add_files([{ file_path: 'test.txt', content: 'new file' }]) end end.to raise_error(Resource::ApiFabricator::ResourceFabricationFailedError, /403 Forbidden - You are not allowed to push into this branch/) diff --git a/qa/qa/specs/features/api/1_manage/users_spec.rb b/qa/qa/specs/features/api/1_manage/users_spec.rb index 531419e8d0f..ff036c18671 100644 --- a/qa/qa/specs/features/api/1_manage/users_spec.rb +++ b/qa/qa/specs/features/api/1_manage/users_spec.rb @@ -4,7 +4,7 @@ require 'airborne' module QA RSpec.describe 'Manage' do - describe 'Users API', :reliable do + describe 'Users API', :reliable, product_group: :workspace do let(:api_client) { Runtime::API::Client.new(:gitlab) } let(:request) { Runtime::API::Request.new(api_client, '/users') } diff --git a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb index 9d47872a774..4ee436a597a 100644 --- a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb +++ b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb @@ -16,18 +16,18 @@ module QA commit.project = project commit.update_files( [ - { - file_path: '.gitlab-ci.yml', - content: 'script' - } + { + file_path: '.gitlab-ci.yml', + content: 'script' + } ] ) commit.add_files( [ - { - file_path: 'foo', - content: 'bar' - } + { + file_path: 'foo', + content: 'bar' + } ] ) end diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb index a1b9e232e3d..c690202f091 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - describe 'Manage', :requires_admin, :reliable do + describe 'Manage', :requires_admin, :reliable, product_group: :import do describe 'Gitlab migration' do let!(:admin_api_client) { Runtime::API::Client.as_admin } let!(:user) do diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb index 2c331584cf7..d684eabe644 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'Project transfer between groups', :reliable do + describe 'Project transfer between groups', :reliable, product_group: :workspace do let(:source_group) do Resource::Group.fabricate_via_api! do |group| group.path = "source-group-#{SecureRandom.hex(8)}" @@ -27,9 +27,7 @@ module QA before do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project - commit.add_files([ - { file_path: 'README.md', content: readme_content } - ]) + commit.add_files([{ file_path: 'README.md', content: readme_content }]) end Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb index 4bfd253c992..b8d00c2faee 100644 --- a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do - describe 'Jenkins integration' do + RSpec.describe 'Manage', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do + describe 'Jenkins integration', product_group: :integrations do let(:jenkins_server) { Service::DockerRun::Jenkins.new } let(:jenkins_client) do @@ -48,7 +48,8 @@ module QA toggle_local_requests(false) end - it 'integrates and displays build status for MR pipeline in GitLab', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347788' do + it 'integrates and displays build status for MR pipeline in GitLab', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347788' do setup_project_integration jenkins_integration = project.find_integration('jenkins') @@ -133,7 +134,8 @@ module QA def patch_host_name(host_name, container_name) return host_name unless host_name.include?('localhost') - ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}`.strip + ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}` + .strip host_name.gsub('localhost', ip_address) end diff --git a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb index 088556a3981..5a4031b4305 100644 --- a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Manage' do include Support::API - describe 'Jira integration', :jira, :orchestrated, :requires_admin do + describe 'Jira integration', :jira, :orchestrated, :requires_admin, product_group: :integrations do let(:jira_project_key) { 'JITP' } let(:project) do Resource::Project.fabricate_via_api! do |project| @@ -15,7 +15,8 @@ module QA before do page.visit Vendor::Jira::JiraAPI.perform(&:base_url) - QA::Support::Retrier.retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, raise_on_failure: true) do + QA::Support::Retrier + .retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, raise_on_failure: true) do page.has_text? 'Welcome to Jira' end @@ -33,10 +34,11 @@ module QA jira.setup_service_with(url: Vendor::Jira::JiraAPI.perform(&:base_url)) end - expect(page).not_to have_text("Requests to the local network are not allowed") + expect(page).not_to have_text("Requests to the local network are not allowed") # rubocop:disable RSpec/ExpectInHook end - it 'closes an issue via pushing a commit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347794' do + it 'closes an issue via pushing a commit', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347794' do issue_key = Vendor::Jira::JiraAPI.perform do |jira_api| jira_api.create_issue(jira_project_key) end @@ -46,7 +48,8 @@ module QA expect_issue_done(issue_key) end - it 'closes an issue via a merge request', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347795' do + it 'closes an issue via a merge request', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347795' do issue_key = Vendor::Jira::JiraAPI.perform do |jira_api| jira_api.create_issue(jira_project_key) end diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb index d8435407296..7e46276be92 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module QA - RSpec.describe 'Plan', :reliable do - describe 'Jira issue import', :jira, :orchestrated, :requires_admin do + RSpec.describe 'Manage', :reliable do + describe 'Jira issue import', :jira, :orchestrated, :requires_admin, product_group: :integrations do let(:jira_project_key) { "JITD" } let(:jira_issue_title) { "[#{jira_project_key}-1] Jira to GitLab Test Issue" } let(:jira_issue_description) { "This issue is for testing importing Jira issues to GitLab." } diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_status_emails_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb index f4794b3a904..4495d83f336 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_status_emails_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb @@ -24,7 +24,7 @@ module QA end end - RSpec.describe 'Verify', :orchestrated, :runner, :requires_admin, :smtp do + RSpec.describe 'Manage', :orchestrated, :runner, :requires_admin, :smtp, product_group: :integrations do describe 'Pipeline status emails' do let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:emails) { %w[foo@bar.com baz@buzz.com] } diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb index ad90df4b90d..56883917153 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb @@ -3,8 +3,12 @@ module QA RSpec.describe 'Manage', :requires_admin, :skip_live_env do describe '2FA' do + let(:admin_api_client) { Runtime::API::Client.as_admin } + let(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner_user) } + let!(:owner_user) do Resource::User.fabricate_via_api! do |usr| + usr.username = "owner_user_#{SecureRandom.hex(4)}" usr.api_client = admin_api_client end end @@ -26,6 +30,7 @@ module QA let(:developer_user) do Resource::User.fabricate_via_api! do |resource| + resource.username = "developer_user_#{SecureRandom.hex(4)}" resource.api_client = admin_api_client end end @@ -38,8 +43,7 @@ module QA it( 'allows enforcing 2FA via UI and logging in with 2FA', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931', - quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369516' } + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931' ) do enforce_two_factor_authentication_on_group(group) @@ -70,14 +74,6 @@ module QA developer_user.remove_via_api! end - def admin_api_client - @admin_api_client ||= Runtime::API::Client.as_admin - end - - def owner_api_client - @owner_api_client ||= Runtime::API::Client.new(:gitlab, user: owner_user) - end - # We are intentionally using the UI to enforce 2FA to exercise the flow with UI. # Any future tests should use the API for this purpose. def enforce_two_factor_authentication_on_group(group) @@ -87,7 +83,9 @@ module QA Page::Group::Menu.perform(&:click_group_general_settings_item) Page::Group::Settings::General.perform(&:set_require_2fa_enabled) - expect(page).to have_text(two_fa_expected_text) + QA::Support::Retrier.retry_on_exception(reload_page: page) do + expect(page).to have_text(two_fa_expected_text) + end Page::Profile::TwoFactorAuth.perform(&:click_configure_it_later_button) diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb index 90fbff3261e..3f461e9247f 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage', :reliable do + RSpec.describe 'Manage', :reliable, product_group: :workspace do describe 'Add project member' do it 'user adds project member', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347887' do Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb index f624f2fb44f..b251b3075dd 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'Create project badge', :reliable do + describe 'Create project badge', :reliable, product_group: :workspace do let(:badge_name) { "project-badge-#{SecureRandom.hex(8)}" } let(:expected_badge_link_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}" } let(:expected_badge_image_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}/badges/main/pipeline.svg" } diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb index d07fff80b19..7c6b0d77219 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage', :smoke do + RSpec.describe 'Manage', :smoke, product_group: :workspace do describe 'Project' do shared_examples 'successful project creation' do it 'creates a new project' do diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb index d299997dd3c..2abbb6ca73c 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage' do + RSpec.describe 'Manage', product_group: :workspace do shared_examples 'loads all images' do |admin| let(:api_client) { Runtime::API::Client.as_admin } diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb index 0477a9b8a1f..6ac11fea7e1 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb @@ -3,7 +3,7 @@ module QA # Spec uses real github.com, which means outage of github can actually block deployment # Keep spec in reliable bucket but don't run in blocking pipelines - RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin do + RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do describe 'Project import' do let(:github_repo) { 'gitlab-qa-github/import-test' } let(:api_client) { Runtime::API::Client.as_admin } diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb index dbfb114dc82..164f86bffce 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'Invite group', :reliable do + describe 'Invite group', :reliable, product_group: :workspace do shared_examples 'invites group to project' do it 'verifies group is added and members can access project with correct access level' do Page::Project::Menu.perform(&:click_members) diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb index 29e590976d2..98a08dd0d9a 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'Project owner permissions', :reliable do + describe 'Project owner permissions', :reliable, product_group: :workspace do let!(:owner) do Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb index 88f4996ff03..33ca5f6009c 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'Project activity', :reliable do + describe 'Project activity', :reliable, product_group: :workspace do it 'user creates an event in the activity page upon Git push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347879' do Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb index a384dc16064..b9b82baa6f1 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'User', :requires_admin do + describe 'User', :requires_admin, product_group: :workspace do let(:admin_api_client) { Runtime::API::Client.as_admin } let(:followed_user_api_client) { Runtime::API::Client.new(:gitlab, user: followed_user) } diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/parent_group_access_termination_spec.rb index 8462f5db30b..54f05f84dca 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/user/parent_group_access_termination_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'User', :requires_admin, :reliable do + describe 'User', :requires_admin, :reliable, product_group: :workspace do let(:admin_api_client) { Runtime::API::Client.as_admin } let!(:user) do @@ -27,7 +27,7 @@ module QA end end - context 'after parent group membership termination' do + context 'for after parent group membership termination' do before do Flow::Login.while_signed_in_as_admin do group.sandbox.visit! @@ -39,7 +39,14 @@ module QA end end - it 'is not allowed to edit the project files', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347866' do + after do + user.remove_via_api! + project.remove_via_api! + group.remove_via_api! + end + + it 'is not allowed to edit the project files', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347866' do Flow::Login.sign_in(as: user) project.visit! @@ -51,12 +58,6 @@ module QA expect(page).to have_text("You can’t edit files directly in this project.") end - - after do - user.remove_via_api! - project.remove_via_api! - group.remove_via_api! - end end end end diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb index 8de9d7c2049..b7585f00630 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Manage' do - describe 'User', :requires_admin do + describe 'User', :requires_admin, product_group: :workspace do let(:admin_api_client) { Runtime::API::Client.as_admin } let!(:sub_group) do diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb index 206e6b8a456..b2c612d38fe 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb @@ -19,12 +19,13 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = template_project commit.commit_message = 'Add custom issue template' - commit.add_files([ - { - file_path: ".gitlab/issue_templates/#{template_name}.md", - content: template_content - } - ]) + commit.add_files( + [ + { + file_path: ".gitlab/issue_templates/#{template_name}.md", + content: template_content + } + ]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb index a1d8b495129..d6e9c1a13df 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb @@ -18,9 +18,7 @@ module QA commit.branch = "development" commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: file_name, content: 'pssst!' } - ]) + commit.add_files([{ file_path: file_name, content: 'pssst!' }]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb index 6ce4217f8ac..d975e18e962 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb @@ -20,12 +20,13 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = template_project commit.commit_message = 'Add custom merge request template' - commit.add_files([ - { - file_path: ".gitlab/merge_request_templates/#{template_name}.md", - content: template_content - } - ]) + commit.add_files( + [ + { + file_path: ".gitlab/merge_request_templates/#{template_name}.md", + content: template_content + } + ]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb index 8885163b5e3..205ff12ff03 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb @@ -16,9 +16,7 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: file_name, content: 'pssst!' } - ]) + commit.add_files([{ file_path: file_name, content: 'pssst!' }]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb new file mode 100644 index 00000000000..37e737a4f84 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Testing wiki content creation inside a project' do + let(:new_wiki_title) { "just_another_wiki_page" } + let(:new_wiki_content) { "this content is changed or added" } + let(:commit_message) { "this is a new addition to the wiki" } + + let(:project) { Resource::Project.fabricate_via_api! } + let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } + + before do + Flow::Login.sign_in + end + + it 'by adding a home page to the wiki', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do + project.visit! + + Page::Project::Menu.perform(&:click_wiki) + Page::Project::Wiki::Show.perform(&:click_create_your_first_page) + + Page::Project::Wiki::Edit.perform do |edit| + edit.set_title new_wiki_title + edit.set_content new_wiki_content + edit.set_message commit_message + end + + Page::Project::Wiki::Edit.perform(&:click_submit) + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_title new_wiki_title + expect(wiki).to have_content new_wiki_content + end + end + + it 'by adding a second page to the wiki', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do + wiki.visit! + + Page::Project::Wiki::Show.perform(&:click_new_page) + + Page::Project::Wiki::Edit.perform do |edit| + edit.set_title new_wiki_title + edit.set_content new_wiki_content + edit.set_message commit_message + end + + Page::Project::Wiki::Edit.perform(&:click_submit) + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_title new_wiki_title + expect(wiki).to have_content new_wiki_content + end + end + + it 'by adding a home page to the wiki using git push', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347806' do + empty_wiki = Resource::Wiki::ProjectPage.new do |empty_wiki| + empty_wiki.project = project + end + + Resource::Repository::WikiPush.fabricate! do |push| + push.file_name = "#{new_wiki_title}.md" + push.file_content = new_wiki_content + push.commit_message = commit_message + push.wiki = empty_wiki + push.new_branch = true + end.visit! + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_title new_wiki_title + expect(wiki).to have_content new_wiki_content + end + end + + it 'by adding a second page to the wiki using git push', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347807' do + Resource::Repository::WikiPush.fabricate! do |push| + push.file_name = "#{new_wiki_title}.md" + push.file_content = new_wiki_content + push.commit_message = commit_message + push.wiki = wiki + push.new_branch = false + end.visit! + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_title new_wiki_title + expect(wiki).to have_content new_wiki_content + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_manipulation_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_manipulation_spec.rb new file mode 100644 index 00000000000..a4bdb0193dd --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_manipulation_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Testing wiki content manipulation inside a project' do + let(:new_wiki_title) { "just_another_wiki_page" } + let(:new_wiki_content) { "this content is changed or added" } + let(:commit_message) { "this is a new addition to the wiki" } + + let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } + + before do + Flow::Login.sign_in + end + + it 'by manipulating content on the page', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347810' do + wiki.visit! + + Page::Project::Wiki::Show.perform(&:click_edit) + + Page::Project::Wiki::Edit.perform do |edit| + edit.set_title new_wiki_title + edit.set_content new_wiki_content + edit.set_message commit_message + end + + Page::Project::Wiki::Edit.perform(&:click_submit) + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_title new_wiki_title + expect(wiki).to have_content new_wiki_content + end + end + + it 'by manipulating content on the page using git push', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347811' do + Resource::Repository::WikiPush.fabricate! do |push| + push.file_content = new_wiki_content + push.commit_message = commit_message + push.wiki = wiki + push.new_branch = false + end.visit! + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_content new_wiki_content + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_directory_management_spec.rb index 2d24f69c883..0af964fc4bf 100644 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_directory_management_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - context 'Wiki' do + describe 'A project wiki' do let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } let(:new_path) { "a/new/path-with-spaces" } @@ -10,7 +10,8 @@ module QA Flow::Login.sign_in end - it 'has changed the directory', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347821' do + it 'can change the directory path of a page', +testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347821' do initial_wiki.visit! Page::Project::Wiki::Show.perform(&:click_edit) diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb index 4b8edf8b792..361fc459d54 100644 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create', :reliable do - context 'Content Editor' do + describe 'Testing project wiki file upload' do let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } let(:page_title) { 'Content Editor Page' } let(:heading_text) { 'My New Heading' } @@ -16,7 +16,8 @@ module QA initial_wiki.project.remove_via_api! end - it 'creates a formatted Wiki page with an image uploaded', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347640' do + it 'by creating a formatted page with an image uploaded', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347640' do initial_wiki.visit! Page::Project::Wiki::Show.perform(&:click_new_page) diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_list_spec.rb index 703d425079d..5c9b97659a7 100644 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_list_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_list_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - context 'Wiki' do + describe 'Project Wiki' do let(:small_number_of_pages) { 5 } let(:large_number_of_pages) { 15 } let(:random_page) { "bulk_#{rand(0..4)}" } @@ -14,8 +14,9 @@ module QA Flow::Login.sign_in end - context 'Sidebar' do - it 'has all expected links that work', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347814' do + context 'with Wiki Sidebar' do + it 'has all expected links that work', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347814' do small_wiki.visit! small_number_of_pages.times do |index| @@ -34,8 +35,9 @@ module QA end end - context 'Page List' do - it 'has all expected links that work', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347813' do + context 'with Wiki Page List' do + it 'has all expected links that work', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347813' do large_wiki.visit! Page::Project::Wiki::Show.perform(&:click_view_all_pages) diff --git a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_page_deletion_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_page_deletion_spec.rb new file mode 100644 index 00000000000..13e04180ab5 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_page_deletion_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Testing project wiki' + let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } + + before do + Flow::Login.sign_in + end + + it 'can delete a page', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347815' do + initial_wiki.visit! + + Page::Project::Wiki::Show.perform(&:click_edit) + Page::Project::Wiki::Edit.perform(&:delete_page) + + Page::Project::Wiki::Show.perform do |wiki| + expect(wiki).to have_no_page + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb index aa332a76c94..0503b1b3761 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb @@ -22,9 +22,7 @@ module QA commit.branch = branch_name commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: 'test-folder/test-file.md', content: 'new content' } - ]) + commit.add_files([{ file_path: 'test-folder/test-file.md', content: 'new content' }]) end project.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb index 1ae1dd87c07..50df8afafaf 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb @@ -33,11 +33,11 @@ module QA end end - context 'on a project with a less commonly used LICENSE', + context 'on a project with an unrecognized LICENSE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366843' do it_behaves_like 'project license detection' do - let(:license_file_name) { 'GFDL-1.2-only' } - let(:rendered_license_name) { 'Other' } + let(:license_file_name) { 'other' } + let(:rendered_license_name) { 'LICENSE' } end end end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb index 65a15ce96a5..f4ca7955a0f 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb @@ -31,7 +31,6 @@ module QA Page::Project::Tag::Show.perform do |show| expect(show).to have_tag_name(tag_name) expect(show).to have_tag_message(tag_message) - expect(show).to have_tag_release_notes(tag_release_notes) expect(show).not_to have_element(:create_tag_button) end end @@ -83,7 +82,6 @@ module QA Page::Project::Tag::New.perform do |new_tag| new_tag.fill_tag_name(name) new_tag.fill_tag_message(message) - new_tag.fill_release_notes(release_notes) new_tag.click_create_tag_button end end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb index 9735aa7959a..561a5a2cc1c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb @@ -22,12 +22,13 @@ module QA before do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project - commit.add_files([ - { - file_path: 'first_directory/test_file.txt', - content: "Test file content" - } - ]) + commit.add_files( + [ + { + file_path: 'first_directory/test_file.txt', + content: "Test file content" + } + ]) end project.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb index fc5754e2c7a..f03c651992c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb @@ -4,18 +4,18 @@ module QA RSpec.describe 'Create' do describe 'Open Web IDE from Diff Tab' do files = [ - { - file_path: 'file1', - content: 'test1' - }, - { - file_path: 'file2', - content: 'test2' - }, - { - file_path: 'file3', - content: 'test3' - } + { + file_path: 'file1', + content: 'test1' + }, + { + file_path: 'file2', + content: 'test2' + }, + { + file_path: 'file3', + content: 'test3' + } ] let(:project) do diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb deleted file mode 100644 index 648ef513e12..00000000000 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_creation_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Create' do - context 'Wiki' do - describe 'testing wiki content creation inside a project' do - let(:new_wiki_title) { "just_another_wiki_page" } - let(:new_wiki_content) { "this content is changed or added" } - let(:commit_message) { "this is a new addition to the wiki" } - - let(:project) { Resource::Project.fabricate_via_api! } - let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } - - before do - Flow::Login.sign_in - end - - it 'by adding a home page to the wiki', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do - project.visit! - - Page::Project::Menu.perform(&:click_wiki) - Page::Project::Wiki::Show.perform(&:click_create_your_first_page) - - Page::Project::Wiki::Edit.perform do |edit| - edit.set_title new_wiki_title - edit.set_content new_wiki_content - edit.set_message commit_message - end - - Page::Project::Wiki::Edit.perform(&:click_submit) - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_title new_wiki_title - expect(wiki).to have_content new_wiki_content - end - end - - it 'by adding a second page to the wiki', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do - wiki.visit! - - Page::Project::Wiki::Show.perform(&:click_new_page) - - Page::Project::Wiki::Edit.perform do |edit| - edit.set_title new_wiki_title - edit.set_content new_wiki_content - edit.set_message commit_message - end - - Page::Project::Wiki::Edit.perform(&:click_submit) - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_title new_wiki_title - expect(wiki).to have_content new_wiki_content - end - end - - it 'by adding a home page to the wiki using git push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347806' do - empty_wiki = Resource::Wiki::ProjectPage.new do |empty_wiki| - empty_wiki.project = project - end - - Resource::Repository::WikiPush.fabricate! do |push| - push.file_name = "#{new_wiki_title}.md" - push.file_content = new_wiki_content - push.commit_message = commit_message - push.wiki = empty_wiki - push.new_branch = true - end.visit! - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_title new_wiki_title - expect(wiki).to have_content new_wiki_content - end - end - - it 'by adding a second page to the wiki using git push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347807' do - Resource::Repository::WikiPush.fabricate! do |push| - push.file_name = "#{new_wiki_title}.md" - push.file_content = new_wiki_content - push.commit_message = commit_message - push.wiki = wiki - push.new_branch = false - end.visit! - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_title new_wiki_title - expect(wiki).to have_content new_wiki_content - end - end - end - end - end -end diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb deleted file mode 100644 index 251728c149f..00000000000 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_content_manipulation_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Create' do - context 'Wiki' do - describe 'testing wiki content manipulation inside a project' do - let(:new_wiki_title) { "just_another_wiki_page" } - let(:new_wiki_content) { "this content is changed or added" } - let(:commit_message) { "this is a new addition to the wiki" } - - let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } - - before do - Flow::Login.sign_in - end - - it 'by manipulating content on the page', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347810' do - wiki.visit! - - Page::Project::Wiki::Show.perform(&:click_edit) - - Page::Project::Wiki::Edit.perform do |edit| - edit.set_title new_wiki_title - edit.set_content new_wiki_content - edit.set_message commit_message - end - - Page::Project::Wiki::Edit.perform(&:click_submit) - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_title new_wiki_title - expect(wiki).to have_content new_wiki_content - end - end - - it 'by manipulating content on the page using git push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347811' do - Resource::Repository::WikiPush.fabricate! do |push| - push.file_content = new_wiki_content - push.commit_message = commit_message - push.wiki = wiki - push.new_branch = false - end.visit! - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_content new_wiki_content - end - end - end - end - end -end diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb deleted file mode 100644 index 78d6d51f260..00000000000 --- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Create' do - context 'Wiki' do - let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! } - - before do - Flow::Login.sign_in - end - - context 'Page deletion' do - it 'has removed the deleted page correctly', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347815' do - initial_wiki.visit! - - Page::Project::Wiki::Show.perform(&:click_edit) - Page::Project::Wiki::Edit.perform(&:delete_page) - - Page::Project::Wiki::Show.perform do |wiki| - expect(wiki).to have_no_page - end - end - end - end - end -end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb index 078465770c6..222d1993bf4 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb @@ -51,16 +51,11 @@ module QA commit.project = package_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: helm_upload_yaml - }, - { - file_path: 'Chart.yaml', - content: helm_chart_yaml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: helm_upload_yaml }, + { file_path: 'Chart.yaml', content: helm_chart_yaml } + ]) end end @@ -94,12 +89,7 @@ module QA commit.project = client_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: helm_install_yaml - } - ]) + commit.add_files([{ file_path: '.gitlab-ci.yml', content: helm_install_yaml }]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb index 921b36b34af..690451f6147 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb @@ -48,26 +48,18 @@ module QA it 'pushes and pulls a maven package', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding) + pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'settings.xml.erb')).result(binding) commit.project = package_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'pom.xml', content: pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end @@ -97,26 +89,18 @@ module QA Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_install_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_install_package.yaml.erb')).result(binding) - client_pom_xml = ERB.new(read_fixture('package_managers/maven', 'client_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/consumer', 'gitlab_ci.yaml.erb')).result(binding) + pom_xml = ERB.new(read_fixture('package_managers/maven/group/consumer', 'pom.xml.erb')).result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven/group/consumer', 'settings.xml.erb')).result(binding) commit.project = client_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_install_package_yaml - }, - { - file_path: 'pom.xml', - content: client_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'pom.xml', content: pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end @@ -143,123 +127,57 @@ module QA end context 'when disabled' do - where do - { - 'using a personal access token' => { - authentication_token_type: :personal_access_token, - maven_header_name: 'Private-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347581' - }, - 'using a project deploy token' => { - authentication_token_type: :project_deploy_token, - maven_header_name: 'Deploy-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347584' - }, - 'using a ci job token' => { - authentication_token_type: :ci_job_token, - maven_header_name: 'Job-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347578' - } - } + before do + Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled) end - with_them do - let(:token) do - case authentication_token_type - when :personal_access_token - personal_access_token - when :ci_job_token - '${env.CI_JOB_TOKEN}' - when :project_deploy_token - project_deploy_token.token - end - end + it 'prevents users from publishing duplicates' do + create_duplicated_package - before do - Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled) - end + push_duplicated_package - it 'prevents users from publishing group level Maven packages duplicates', testcase: params[:testcase] do - create_duplicated_package - - push_duplicated_package - - client_project.visit! + client_project.visit! - show_latest_deploy_job + show_latest_deploy_job - Page::Project::Job::Show.perform do |job| - expect(job).not_to be_successful(timeout: 800) - end + Page::Project::Job::Show.perform do |job| + expect(job).not_to be_successful(timeout: 800) end end end context 'when enabled' do - where do - { - 'using a personal access token' => { - authentication_token_type: :personal_access_token, - maven_header_name: 'Private-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347580' - }, - 'using a project deploy token' => { - authentication_token_type: :project_deploy_token, - maven_header_name: 'Deploy-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347583' - }, - 'using a ci job token' => { - authentication_token_type: :ci_job_token, - maven_header_name: 'Job-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347577' - } - } + before do + Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled) end - with_them do - let(:token) do - case authentication_token_type - when :personal_access_token - personal_access_token - when :ci_job_token - '${env.CI_JOB_TOKEN}' - when :project_deploy_token - project_deploy_token.token - end - end - - before do - Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled) - end - - it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do - create_duplicated_package + it 'allows users to publish duplicates' do + create_duplicated_package - push_duplicated_package + push_duplicated_package - show_latest_deploy_job + show_latest_deploy_job - Page::Project::Job::Show.perform do |job| - expect(job).to be_successful(timeout: 800) - end + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) end end end def create_duplicated_package - settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven', 'settings_with_pat.xml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) + settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding) + pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding) with_fixtures([ - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml_with_pat - } - ]) do |dir| + { + file_path: 'pom.xml', + content: pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml_with_pat + } + ]) do |dir| Service::DockerRun::Maven.new(dir).publish! end @@ -275,26 +193,18 @@ module QA def push_duplicated_package Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| - maven_upload_package_yaml = ERB.new(read_fixture('package_managers/maven', 'maven_upload_package.yaml.erb')).result(binding) - package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) - settings_xml = ERB.new(read_fixture('package_managers/maven', 'settings.xml.erb')).result(binding) + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding) + pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding) + settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding) commit.project = client_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'pom.xml', content: pom_xml }, + { file_path: 'settings.xml', content: settings_xml_with_pat } + ]) end end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb index 13607ba1b41..324e881f160 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb @@ -1,8 +1,14 @@ # frozen_string_literal: true module QA - RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable do + RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, + feature_flag: { + name: 'maven_central_request_forwarding', + scope: :global + } do describe 'Maven project level endpoint' do + include Runtime::Fixtures + let(:group_id) { 'com.gitlab.qa' } let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" } let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') } @@ -51,54 +57,6 @@ module QA end end - let(:gitlab_ci_file) do - { - file_path: '.gitlab-ci.yml', - content: - <<~YAML - deploy-and-install: - image: maven:3.6-jdk-11 - script: - - 'mvn deploy -s settings.xml' - - 'mvn install -s settings.xml' - only: - - "#{package_project.default_branch}" - tags: - - "runner-for-#{package_project.name}" - YAML - } - end - - let(:pom_file) do - { - file_path: 'pom.xml', - content: <<~XML - <project> - <groupId>#{group_id}</groupId> - <artifactId>#{artifact_id}</artifactId> - <version>#{package_version}</version> - <modelVersion>4.0.0</modelVersion> - <repositories> - <repository> - <id>#{package_project.name}</id> - <url>#{gitlab_address_with_port}/api/v4/projects/#{package_project.id}/-/packages/maven</url> - </repository> - </repositories> - <distributionManagement> - <repository> - <id>#{package_project.name}</id> - <url>#{gitlab_address_with_port}/api/v4/projects/#{package_project.id}/packages/maven</url> - </repository> - <snapshotRepository> - <id>#{package_project.name}</id> - <url>#{gitlab_address_with_port}/api/v4/projects/#{package_project.id}/packages/maven</url> - </snapshotRepository> - </distributionManagement> - </project> - XML - } - end - before do Flow::Login.sign_in_unless_signed_in runner @@ -142,40 +100,24 @@ module QA end end - let(:settings_xml) do - { - file_path: 'settings.xml', - content: <<~XML - <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> - <servers> - <server> - <id>#{package_project.name}</id> - <configuration> - <httpHeaders> - <property> - <name>#{maven_header_name}</name> - <value>#{token}</value> - </property> - </httpHeaders> - </configuration> - </server> - </servers> - </settings> - XML - } - end - it 'pushes and pulls a maven package via maven', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/project', 'gitlab_ci.yaml.erb')) + .result(binding) + pom_xml = ERB.new(read_fixture('package_managers/maven/project', 'pom.xml.erb')) + .result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven/project', 'settings.xml.erb')) + .result(binding) + commit.project = package_project - commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - gitlab_ci_file, - pom_file, - settings_xml - ]) + commit.commit_message = 'Add files' + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'pom.xml', content: pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end @@ -184,17 +126,7 @@ module QA Flow::Pipeline.visit_latest_pipeline Page::Project::Pipeline::Show.perform do |pipeline| - pipeline.click_job('deploy') - end - - Page::Project::Job::Show.perform do |job| - expect(job).to be_successful(timeout: 800) - - job.click_element(:pipeline_path) - end - - Page::Project::Pipeline::Show.perform do |pipeline| - pipeline.click_job('install') + pipeline.click_job('deploy-and-install') end Page::Project::Job::Show.perform do |job| @@ -215,5 +147,107 @@ module QA end end end + + describe 'Maven request forwarding' do + include Runtime::Fixtures + + let(:group_id) { 'com.gitlab.qa' } + let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" } + let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') } + let(:package_version) { '1.3.7' } + let(:package_type) { 'maven' } + let(:personal_access_token) { Runtime::Env.personal_access_token } + let(:group) { Resource::Group.fabricate_via_api! } + + let(:gitlab_address_with_port) do + uri = URI.parse(Runtime::Scenario.gitlab_address) + "#{uri.scheme}://#{uri.host}:#{uri.port}" + end + + let(:package) do + Resource::Package.init do |package| + package.name = package_name + package.project = imported_project + end + end + + let(:runner) do + Resource::Runner.fabricate! do |runner| + runner.name = "qa-runner-#{Time.now.to_i}" + runner.tags = ["runner-for-#{imported_project.name}"] + runner.executor = :docker + runner.token = group.reload!.runners_token + end + end + + let(:imported_project) do + Resource::ProjectImportedFromURL.fabricate_via_browser_ui! do |project| + project.name = "#{package_type}_imported_project" + project.group = group + project.gitlab_repository_path = 'https://gitlab.com/gitlab-org/quality/imported-projects/maven.git' + end + end + + before do + Runtime::Feature.enable(:maven_central_request_forwarding) + Flow::Login.sign_in_unless_signed_in + + imported_project + runner + end + + after do + Runtime::Feature.disable(:maven_central_request_forwarding) + + runner.remove_via_api! + package.remove_via_api! + imported_project.remove_via_api! + end + + it( + 'uses GitLab as a mirror of the central proxy', + :skip_live_env, + quarantine: { + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/378221', + type: :investigating + }, + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/375767' + ) do + Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do + Resource::Repository::Commit.fabricate_via_api! do |commit| + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/project/request_forwarding', + 'gitlab_ci.yaml.erb' + ) + ) + .result(binding) + settings_xml = ERB.new(read_fixture('package_managers/maven/project/request_forwarding', + 'settings.xml.erb' + ) + ) + .result(binding) + + commit.project = imported_project + commit.commit_message = 'Add files' + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'settings.xml', content: settings_xml } + ]) + end + end + + imported_project.visit! + + Flow::Pipeline.visit_latest_pipeline + + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('install') + end + + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end + end + end end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb index 45693ecee41..22052aa4110 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb @@ -34,20 +34,15 @@ module QA it 'pushes and pulls a maven package via gradle', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| - gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven', 'gradle_upload_package.yaml.erb')).result(binding) - build_upload_gradle = ERB.new(read_fixture('package_managers/maven', 'build_upload.gradle.erb')).result(binding) + gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven/gradle', 'gradle_upload_package.yaml.erb')).result(binding) + build_upload_gradle = ERB.new(read_fixture('package_managers/maven/gradle', 'build_upload.gradle.erb')).result(binding) commit.project = package_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: gradle_upload_yaml - }, - { - file_path: 'build.gradle', - content: build_upload_gradle - } + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gradle_upload_yaml }, + { file_path: 'build.gradle', content: build_upload_gradle } ]) end end @@ -78,21 +73,16 @@ module QA Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| - gradle_install_yaml = ERB.new(read_fixture('package_managers/maven', 'gradle_install_package.yaml.erb')).result(binding) - build_install_gradle = ERB.new(read_fixture('package_managers/maven', 'build_install.gradle.erb')).result(binding) + gradle_install_yaml = ERB.new(read_fixture('package_managers/maven/gradle', 'gradle_install_package.yaml.erb')).result(binding) + build_install_gradle = ERB.new(read_fixture('package_managers/maven/gradle', 'build_install.gradle.erb')).result(binding) commit.project = client_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: gradle_install_yaml - }, - { - file_path: 'build.gradle', - content: build_install_gradle - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gradle_install_yaml }, + { file_path: 'build.gradle', content: build_install_gradle } + ]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb index f229f30facc..e2a7006249d 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb @@ -105,14 +105,7 @@ module QA nuget_upload_yaml = ERB.new(read_fixture('package_managers/nuget', 'nuget_upload_package.yaml.erb')).result(binding) commit.project = project commit.commit_message = 'Add .gitlab-ci.yml' - commit.update_files( - [ - { - file_path: '.gitlab-ci.yml', - content: nuget_upload_yaml - } - ] - ) + commit.update_files([{ file_path: '.gitlab-ci.yml', content: nuget_upload_yaml }]) end end @@ -137,9 +130,9 @@ module QA commit.commit_message = 'Add new csproj file' commit.add_files( [ - { - file_path: 'otherdotnet.csproj', - content: <<~EOF + { + file_path: 'otherdotnet.csproj', + content: <<~EOF <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> @@ -148,18 +141,11 @@ module QA </PropertyGroup> </Project> - EOF - } - ] - ) - commit.update_files( - [ - { - file_path: '.gitlab-ci.yml', - content: nuget_install_yaml - } + EOF + } ] ) + commit.update_files([{ file_path: '.gitlab-ci.yml', content: nuget_install_yaml }]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb index e70b95db1a5..620bb7e4988 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb @@ -101,9 +101,9 @@ module QA commit.commit_message = 'Add files' commit.update_files( [ - { - file_path: '.gitlab-ci.yml', - content: <<~YAML + { + file_path: '.gitlab-ci.yml', + content: <<~YAML stages: - deploy - install @@ -132,11 +132,11 @@ module QA - if: '$CI_COMMIT_BRANCH == "#{project.default_branch}"' tags: - "runner-for-#{project.name}" - YAML - }, - { - file_path: 'dotnetcore.csproj', - content: <<~EOF + YAML + }, + { + file_path: 'dotnetcore.csproj', + content: <<~EOF <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> @@ -145,8 +145,8 @@ module QA </PropertyGroup> </Project> - EOF - } + EOF + } ] ) end diff --git a/qa/qa/specs/features/sanity/framework_spec.rb b/qa/qa/specs/features/sanity/framework_spec.rb index feec56478c0..fa34f525a85 100644 --- a/qa/qa/specs/features/sanity/framework_spec.rb +++ b/qa/qa/specs/features/sanity/framework_spec.rb @@ -6,7 +6,7 @@ module QA it 'succeeds' do Runtime::Browser.visit(:gitlab, Page::Main::Login) - expect(page).to have_text('A complete DevOps platform') + expect(page).to have_text('GitLab') end end diff --git a/qa/qa/specs/qa_deprecation_toolkit_env.rb b/qa/qa/specs/qa_deprecation_toolkit_env.rb index 21ef5a6f229..5224a2e9ae0 100644 --- a/qa/qa/specs/qa_deprecation_toolkit_env.rb +++ b/qa/qa/specs/qa_deprecation_toolkit_env.rb @@ -5,20 +5,24 @@ require 'deprecation_toolkit/rspec' require 'concurrent/utility/monotonic_time' require 'active_support/gem_version' -module QaDeprecationToolkitEnv - # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 - # rubocop:disable Layout/LineLength - def self.kwargs_warning - %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} - end - # rubocop:enable Layout/LineLength +module QA + module Specs + class QaDeprecationToolkitEnv + # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 + # rubocop:disable Layout/LineLength + def self.kwargs_warning + %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} + end + # rubocop:enable Layout/LineLength - def self.configure! - # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7 - Warning[:deprecated] = true + def self.configure! + # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7 + Warning[:deprecated] = true - DeprecationToolkit::Configuration.test_runner = :rspec - DeprecationToolkit::Configuration.deprecation_path = 'deprecations' - DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning] + DeprecationToolkit::Configuration.test_runner = :rspec + DeprecationToolkit::Configuration.deprecation_path = 'deprecations' + DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning] + end + end end end diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb index b9e67c2fa72..97901042883 100644 --- a/qa/qa/specs/spec_helper.rb +++ b/qa/qa/specs/spec_helper.rb @@ -2,8 +2,7 @@ require_relative '../../qa' -require_relative 'qa_deprecation_toolkit_env' -QaDeprecationToolkitEnv.configure! +QA::Specs::QaDeprecationToolkitEnv.configure! Knapsack::Adapters::RSpecAdapter.bind if QA::Runtime::Env.knapsack? diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb index 8fa6d3f23dc..ea19d9ef332 100644 --- a/qa/qa/support/api.rb +++ b/qa/qa/support/api.rb @@ -23,7 +23,7 @@ module QA verify_ssl: false } - RestClient::Request.execute(default_args.merge(args)) + RestClient::Request.execute(default_args.merge(with_canary(args))) rescue StandardError => e return_response_or_raise(e) end @@ -37,21 +37,22 @@ module QA verify_ssl: false } - RestClient::Request.execute( - default_args.merge(args) - ) + RestClient::Request.execute(default_args.merge(with_canary(args))) rescue StandardError => e return_response_or_raise(e) end end - def patch(url, payload = nil) + def patch(url, payload = nil, args = {}) with_retry_on_too_many_requests do - RestClient::Request.execute( + default_args = { method: :patch, url: url, payload: payload, - verify_ssl: false) + verify_ssl: false + } + + RestClient::Request.execute(default_args.merge(with_canary(args))) rescue StandardError => e return_response_or_raise(e) end @@ -66,7 +67,7 @@ module QA verify_ssl: false } - RestClient::Request.execute(default_args.merge(args)) + RestClient::Request.execute(default_args.merge(with_canary(args))) rescue StandardError => e return_response_or_raise(e) end @@ -98,6 +99,14 @@ module QA url.sub(/private_token=[^&]*/, "private_token=[****]") end + # Merges the gitlab_canary cookie into existing cookies for mixed environment testing. + # + # @param [Hash] args the existing args passed to method + # @return [Hash] args or args with merged canary cookie if it exists + def with_canary(args) + args.deep_merge(cookies: QA::Runtime::Env.canary_cookie) + end + def with_retry_on_too_many_requests response = nil diff --git a/qa/qa/support/formatters/test_stats_formatter.rb b/qa/qa/support/formatters/test_stats_formatter.rb index 9d19c2e8bb5..2cde2d0928e 100644 --- a/qa/qa/support/formatters/test_stats_formatter.rb +++ b/qa/qa/support/formatters/test_stats_formatter.rb @@ -84,6 +84,7 @@ module QA job_url: QA::Runtime::Env.ci_job_url, pipeline_url: env('CI_PIPELINE_URL'), pipeline_id: env('CI_PIPELINE_ID'), + job_id: env('CI_JOB_ID'), merge_request_iid: merge_request_iid } } diff --git a/qa/qa/support/repeater.rb b/qa/qa/support/repeater.rb index 6956987de05..26d447f8e0f 100644 --- a/qa/qa/support/repeater.rb +++ b/qa/qa/support/repeater.rb @@ -52,12 +52,13 @@ module QA sleep_and_reload_if_needed(sleep_interval, reload_page) attempts += 1 end - rescue StandardError, RSpec::Expectations::ExpectationNotMetError + rescue StandardError, RSpec::Expectations::ExpectationNotMetError => e raise unless retry_on_exception attempts += 1 raise unless remaining_attempts?(attempts, max_attempts) && remaining_time?(start, max_duration) + QA::Runtime::Logger.debug("Retry block rescued following error: #{e}, trying again...") sleep_and_reload_if_needed(sleep_interval, reload_page) retry end diff --git a/qa/qa/tools/ci/non_empty_suites.rb b/qa/qa/tools/ci/non_empty_suites.rb index 687c11a3e62..2319237fa25 100644 --- a/qa/qa/tools/ci/non_empty_suites.rb +++ b/qa/qa/tools/ci/non_empty_suites.rb @@ -10,42 +10,11 @@ module QA class NonEmptySuites include Helpers - # rubocop:disable Layout/LineLength - SCENARIOS = [ - { klass: "Test::Instance::All" }, - { klass: "Test::Instance::Smoke" }, - { klass: "Test::Instance::Reliable" }, - { klass: "Test::Instance::ReviewBlocking" }, - { klass: "Test::Instance::ReviewNonBlocking" }, - { klass: "Test::Instance::CloudActivation" }, - { klass: "Test::Instance::Integrations" }, - { klass: "Test::Instance::Jira" }, - { klass: "Test::Instance::LargeSetup" }, - { klass: "Test::Instance::Metrics" }, - { klass: "Test::Instance::ObjectStorage" }, - { klass: "Test::Instance::Packages" }, - { klass: "Test::Instance::RepositoryStorage" }, - { klass: "Test::Integration::ServicePingDisabled" }, - { klass: "Test::Integration::LDAPNoTLS" }, - { klass: "Test::Integration::LDAPTLS" }, - { klass: "Test::Integration::LDAPNoServer" }, - { klass: "Test::Integration::InstanceSAML" }, - { klass: "Test::Integration::RegistryWithCDN" }, - { klass: "Test::Integration::RegistryTLS" }, - { klass: "Test::Integration::Registry" }, - { klass: "Test::Integration::SMTP" }, - { klass: "QA::EE::Scenario::Test::Integration::Elasticsearch" }, - { klass: "QA::EE::Scenario::Test::Integration::GroupSAML" }, - { - klass: "QA::EE::Scenario::Test::Geo", - args: "--primary-address http://dummy1.test --primary-name gitlab-primary --secondary-address http://dummy2.test --secondary-name gitlab-secondary --without-setup" - }, - { - klass: "Test::Integration::Mattermost", - args: "--mattermost-address http://mattermost.test" - } + # @return [Array] scenarios that never run in package-and-test pipeline + IGNORED_SCENARIOS = [ + "QA::EE::Scenario::Test::Geo", + "QA::Scenario::Test::Instance::Airgapped" ].freeze - # rubocop:enable Layout/LineLength def initialize(qa_tests) @qa_tests = qa_tests @@ -56,38 +25,58 @@ module QA # @return [String] def fetch logger.info("Checking for runnable suites") - scenarios = SCENARIOS.each_with_object([]) do |scenario, runnable_scenarios| - logger.info(" fetching runnable specs for '#{scenario[:klass]}'") + scenarios.each_with_object([]) do |scenario, runnable_scenarios| + logger.info(" fetching runnable specs for '#{scenario}'") + next logger.info(" scenario is in ignore list, skipping") if IGNORED_SCENARIOS.include?(scenario) - out, err, status = run_command(**scenario) + out, err, status = run_command(scenario) unless status.success? - logger.error(" example count failed!\n#{err}") + logger.error(" example count failed!\n#{err}") next end count = out.split("\n").last.to_i logger.info(" found #{count} examples to run") - runnable_scenarios << scenario[:klass] if count > 0 - end - - scenarios.join(",") + runnable_scenarios << scenario if count > 0 + end.join(",") end private attr_reader :qa_tests + # Get all defined scenarios + # + # @return [Array<String>] + def scenarios + foss_scenarios = scenario_classes(QA::Scenario::Test) + return foss_scenarios unless QA.const_defined?("QA::EE") + + foss_scenarios + scenario_classes(QA::EE::Scenario::Test) + end + + # Fetch scenario classes recursively + # + # @param [Module] mod + # @return [Array<String>] + def scenario_classes(mod) + mod.constants.map do |const| + c = mod.const_get(const, false) + next c.to_s if c.is_a?(Class) + + scenario_classes(c) + end.flatten + end + # Run scenario count command # # @param [String] klass - # @param [String] args # @return [String] - def run_command(klass:, args: nil) + def run_command(klass) cmd = ["bundle exec bin/qa"] cmd << klass cmd << "--count-examples-only --address http://dummy1.test" - cmd << args if args cmd << "-- #{qa_tests}" unless qa_tests.blank? Open3.capture3(cmd.join(" ")) diff --git a/qa/qa/tools/ci/qa_changes.rb b/qa/qa/tools/ci/qa_changes.rb index 75274961efe..784923714d6 100644 --- a/qa/qa/tools/ci/qa_changes.rb +++ b/qa/qa/tools/ci/qa_changes.rb @@ -11,17 +11,25 @@ module QA QA_PATTERN = %r{^qa/}.freeze SPEC_PATTERN = %r{^qa/qa/specs/features/}.freeze - - def initialize(mr_diff, mr_labels) + DEPENDENCY_PATTERN = Regexp.union( + /_VERSION/, + /Gemfile\.lock/, + /yarn\.lock/, + /Dockerfile\.assets/ + ) + + def initialize(mr_diff, mr_labels, additional_group_spec_list) @mr_diff = mr_diff @mr_labels = mr_labels + @additional_group_spec_list = additional_group_spec_list end # Specific specs to run # # @return [String] def qa_tests - return if mr_diff.empty? + return if mr_diff.empty? || dependency_changes + # make paths relative to qa directory return changed_files&.map { |path| path.delete_prefix("qa/") }&.join(" ") if only_spec_changes? return qa_spec_directories_for_devops_stage&.join(" ") if non_qa_changes? && mr_labels.any? @@ -73,6 +81,9 @@ module QA # @return [Array] attr_reader :mr_labels + # @return [Hash<String, Array<String>>] + attr_reader :additional_group_spec_list + # Are the changed files only qa specs? # # @return [Boolean] whether the changes files are only qa specs @@ -94,6 +105,13 @@ module QA mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::') end + # Extract group name from MR labels + # + # @return [String] a group name + def group_name_from_mr_labels + mr_labels.find { |label| label =~ /^group::/ }&.delete_prefix('group::') + end + # Get qa spec directories for devops stage # # @return [Array] qa spec directories @@ -101,14 +119,37 @@ module QA devops_stage = devops_stage_from_mr_labels return unless devops_stage - Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} } + spec_dirs = stage_specs(devops_stage) + + grp_name = group_name_from_mr_labels + return spec_dirs if grp_name.nil? + + additional_grp_specs = additional_group_spec_list[grp_name] + return spec_dirs if additional_grp_specs.nil? + + spec_dirs + stage_specs(*additional_grp_specs) + end + + # Changes to gitlab dependencies + # + # @return [Boolean] + def dependency_changes + changed_files.any? { |file| file.match?(DEPENDENCY_PATTERN) } end # Change files in merge request # # @return [Array<String>] def changed_files - @changed_files ||= mr_diff.map { |change| change[:path] } # rubocop:disable Rails/Pluck + @changed_files ||= mr_diff.map { |change| change[:path] } + end + + # Devops stage specs + # + # @param [Array<String>] devops_stages + # @return [Array] + def stage_specs(*devops_stages) + Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_(#{devops_stages.join('|')})/$} } end end end diff --git a/qa/qa/tools/delete_test_ssh_keys.rb b/qa/qa/tools/delete_test_ssh_keys.rb index 9e5728a5509..c10188eae6d 100644 --- a/qa/qa/tools/delete_test_ssh_keys.rb +++ b/qa/qa/tools/delete_test_ssh_keys.rb @@ -12,7 +12,7 @@ module QA module Tools - class DeleteTestSSHKeys + class DeleteTestSshKeys include Support::API ITEMS_PER_PAGE = '100' diff --git a/qa/qa/tools/initialize_gitlab_auth.rb b/qa/qa/tools/initialize_gitlab_auth.rb index 18e90f0d739..6d586f95ecf 100644 --- a/qa/qa/tools/initialize_gitlab_auth.rb +++ b/qa/qa/tools/initialize_gitlab_auth.rb @@ -6,7 +6,7 @@ module QA # Also creates a personal access token # @example # $ bundle exec rake 'initialize_gitlab_auth[http://gitlab.test]' - class InitializeGitLabAuth + class InitializeGitlabAuth attr_reader :address def initialize(address:) diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb index 60c6dbfc16c..068fe37a37b 100644 --- a/qa/qa/tools/test_resources_handler.rb +++ b/qa/qa/tools/test_resources_handler.rb @@ -25,13 +25,17 @@ module QA module Tools class TestResourcesHandler include Support::API - - IGNORED_RESOURCES = [ - 'QA::Resource::CiVariable', - 'QA::Resource::Repository::Commit', - 'QA::EE::Resource::GroupIteration', - 'QA::EE::Resource::Settings::Elasticsearch', - 'QA::EE::Resource::VulnerabilityItem' + include Ci::Helpers + + IGNORED_RESOURCES = %w[ + QA::Resource::CiVariable + QA::Resource::Repository::Commit + QA::Resource::Design + QA::EE::Resource::GroupIteration + QA::EE::Resource::Settings::Elasticsearch + QA::EE::Resource::VulnerabilityItem + QA::EE::Resource::ScanResultPolicyProject + QA::EE::Resource::ScanResultPolicyCommit ].freeze PROJECT = 'gitlab-qa-resources' @@ -44,10 +48,19 @@ module QA def run_delete failures = files.flat_map do |file| resources = read_file(file) - next if resources.nil? + if resources.nil? + logger.info("#{file} is empty, next...") + next + end filtered_resources = filter_resources(resources) + if filtered_resources.nil? + logger.info("No resources left to delete after filtering!") + next + end + delete_resources(filtered_resources) + delete_groups_permanently(filtered_resources['QA::Resource::Group']) end return puts "\nDone" if failures.empty? @@ -62,17 +75,15 @@ module QA # E.g: staging/failed-test-resources-<randomhex>.json def upload(ci_project_name) if files.empty? - puts "\nNothing to upload!" - exit 0 + logger.info("\nNothing to upload!") + return end files.each do |file| file_name = "#{ci_project_name}/#{file.split('/').last}" - Runtime::Logger.info("Uploading #{file_name}...") + logger.info("Uploading #{file_name}...") gcs_storage.put_object(BUCKET, file_name, File.read(file)) end - - puts "\nDone" end # Download files from GCS bucket by environment name @@ -85,40 +96,38 @@ module QA end if files_list.blank? - puts "\nNothing to download!" - exit 0 + logger.info("\nNothing to download!") + return end FileUtils.mkdir_p('tmp/') files_list.each do |file_name| local_path = "tmp/#{file_name.split('/').last}" - Runtime::Logger.info("Downloading #{file_name} to #{local_path}") + logger.info("Downloading #{file_name} to #{local_path}") file = gcs_storage.get_object(BUCKET, file_name) File.write(local_path, file[:body]) - Runtime::Logger.info("Deleting #{file_name} from bucket") + logger.info("Deleting #{file_name} from bucket") gcs_storage.delete_object(BUCKET, file_name) end - - puts "\nDone" end private def files - Runtime::Logger.info('Gathering JSON files...') + logger.info('Gathering JSON files...') files = Dir.glob(@file_pattern) if files.empty? - puts "There is no file with this pattern #{@file_pattern}" + logger.info("There is no file with this pattern #{@file_pattern}") exit 0 end files.reject! { |file| File.zero?(file) } if files.empty? - puts "\nAll files were empty and rejected, nothing more to do!" + logger.info("\nAll files were empty and rejected, nothing more to do!") exit 0 end @@ -126,14 +135,15 @@ module QA end def read_file(file) + logger.info("Reading and processing #{file}...") JSON.parse(File.read(file)) rescue JSON::ParserError - Runtime::Logger.error("Failed to read #{file} - Invalid format") + logger.error("Failed to read #{file} - Invalid format") nil end def filter_resources(resources) - Runtime::Logger.info('Filtering resources - Only keep deletable resources...') + logger.info('Filtering resources - Only keep deletable resources...') transformed_values = resources.transform_values! do |v| v.reject do |attributes| @@ -147,28 +157,55 @@ module QA end def delete_resources(resources) - if resources.nil? - puts "\nNo resources left to delete after filtering!" - exit 0 - end - resources.each_with_object([]) do |(key, value), failures| value.each do |resource| - next if resource_not_found?(resource['api_path']) - resource_info = resource['info'] ? "#{key} - #{resource['info']}" : "#{key} at #{resource['api_path']}" + logger.info("Processing #{resource_info}...") + + if resource_not_found?(resource['api_path']) + logger.info("#{resource['api_path']} returns 404, next...") + next + end + delete_response = delete(Runtime::API::Request.new(api_client, resource['api_path']).url) if delete_response.code == 202 || delete_response.code == 204 - Runtime::Logger.info("Deleting #{resource_info}... SUCCESS") + if key == 'QA::Resource::Group' && !resource_not_found?(resource['api_path']) + logger.info("Successfully marked #{resource_info} for deletion...") + else + logger.info("Deleting #{resource_info}... \e[32mSUCCESS\e[0m") + end else - Runtime::Logger.info("Deleting #{resource_info}... FAILED - #{delete_response}") - failures << resource_info + logger.info("Deleting #{resource_info}... \e[31mFAILED - #{delete_response}\e[0m") + # We might try to delete some groups already marked for deletion, it's fine to ignore these failures + failures << resource_info unless key == 'QA::Resource::Group' end end end end + def delete_groups_permanently(groups) + groups.each_with_object([]) do |group, failures| + logger.info("Processing QA::Resource::Group #{group['info']}...") + + if resource_not_found?(group['api_path']) + logger.info("#{group['api_path']} returns 404, next...") + next + end + + permanent_delete_path = "#{group['api_path']}?permanently_remove=true"\ + "&full_path=#{group['info'].split("'").last}" + response = delete(Runtime::API::Request.new(api_client, permanent_delete_path).url) + + if response.code == 202 + logger.info("Permanently deleting group #{group['info']}... \e[32mSUCCESS\e[0m") + else + logger.info("Permanently deleting group #{group['info']}... \e[31mFAILED - #{response}\e[0m") + failures << "QA::Resource::Group #{group['info']}" + end + end + end + def resource_not_found?(api_path) # if api path contains param "?hard_delete=<boolean>", remove it get(Runtime::API::Request.new(api_client, api_path.split('?').first).url).code.eql? 404 diff --git a/qa/qa/vendor/jira/jira_issue_page.rb b/qa/qa/vendor/jira/jira_issue_page.rb new file mode 100644 index 00000000000..5f5449513ff --- /dev/null +++ b/qa/qa/vendor/jira/jira_issue_page.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'capybara/dsl' + +module QA + module Vendor + module Jira + class JiraIssuePage < JiraAPI + include Capybara::DSL + include Scenario::Actable + + def login(username, password) + QA::Runtime::Logger.debug("Logging into JIRA with username: #{username} and password:#{password}") + + fill_in 'login-form-username', with: username + fill_in 'login-form-password', with: password + click_on 'login-form-submit' + end + + def go_to_login_page + click_on 'log in' + end + + def login_if_required(username, password) + return unless login_required? + + go_to_login_page + login(username, password) + end + + def summary_field + page.find('#summary').value + end + + def issue_description + page.find('#description', visible: false).value + end + + def login_required? + login_required = page.has_text?('You are not logged in') + QA::Runtime::Logger.debug("login_required: #{login_required}") + login_required + end + end + end + end +end diff --git a/qa/spec/resource/user_spec.rb b/qa/spec/resource/user_spec.rb index c0140abf298..d82dcc3b21e 100644 --- a/qa/spec/resource/user_spec.rb +++ b/qa/spec/resource/user_spec.rb @@ -23,8 +23,8 @@ RSpec.describe QA::Resource::User do end describe '#password' do - it 'generates a random 16 character password by default' do - expect(subject.password).to match(/\w{16}/) + it 'generates a default password' do + expect(subject.password).to match('Pa$$w0rd') end it 'is possible to set the password' do diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index a41d7385c41..61d91da8738 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -339,4 +339,35 @@ RSpec.describe QA::Runtime::Env do end end end + + describe '.canary_cookie' do + subject { described_class.canary_cookie } + + context 'with QA_COOKIES set' do + using RSpec::Parameterized::TableSyntax + + where(:cookie_value, :result) do + 'gitlab_canary=true' | { gitlab_canary: "true" } + 'other_cookie=value\;gitlab_canary=true' | { gitlab_canary: "true" } + 'gitlab_canary=false' | { gitlab_canary: "false" } + 'gitlab_canary=false\;other_cookie=value' | { gitlab_canary: "false" } + end + + with_them do + before do + stub_env('QA_COOKIES', cookie_value) + end + + it { is_expected.to eq(result) } + end + end + + context 'without QA_COOKIES set' do + before do + stub_env('QA_COOKIES', nil) + end + + it { is_expected.to be_empty } + end + end end diff --git a/qa/spec/scenario/test/integration/github_spec.rb b/qa/spec/scenario/test/integration/github_spec.rb deleted file mode 100644 index 4ad42b4f5cc..00000000000 --- a/qa/spec/scenario/test/integration/github_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe QA::Scenario::Test::Integration::Github do - describe '#perform' do - let(:env) { spy('Runtime::Env', knapsack?: false, dry_run: false) } - - before do - stub_const('QA::Runtime::Env', env) - end - - it_behaves_like 'a QA scenario class' do - let(:tags) { [:github] } - - it 'requires a GitHub access token' do - subject.perform(args) - - expect(env).to have_received(:require_github_access_token!) - end - end - end -end diff --git a/qa/spec/support/formatters/test_stats_formatter_spec.rb b/qa/spec/support/formatters/test_stats_formatter_spec.rb index ba59588d186..d0d89b5ee73 100644 --- a/qa/spec/support/formatters/test_stats_formatter_spec.rb +++ b/qa/spec/support/formatters/test_stats_formatter_spec.rb @@ -15,6 +15,7 @@ describe QA::Support::Formatters::TestStatsFormatter do let(:ci_job_url) { 'url' } let(:ci_pipeline_url) { 'url' } let(:ci_pipeline_id) { '123' } + let(:ci_job_id) { '321' } let(:run_type) { 'staging-full' } let(:smoke) { 'false' } let(:reliable) { 'false' } @@ -64,6 +65,7 @@ describe QA::Support::Formatters::TestStatsFormatter do job_url: ci_job_url, pipeline_url: ci_pipeline_url, pipeline_id: ci_pipeline_id, + job_id: ci_job_id, merge_request_iid: nil } } @@ -80,7 +82,7 @@ describe QA::Support::Formatters::TestStatsFormatter do around do |example| RSpec::Core::Sandbox.sandboxed do |config| - config.formatter = QA::Support::Formatters::TestStatsFormatter + config.formatter = described_class config.before(:context) { RSpec.current_example = nil } example.run @@ -125,6 +127,7 @@ describe QA::Support::Formatters::TestStatsFormatter do stub_env('CI_JOB_NAME', ci_job_name) stub_env('CI_PIPELINE_URL', ci_pipeline_url) stub_env('CI_PIPELINE_ID', ci_pipeline_id) + stub_env('CI_JOB_ID', ci_job_id) stub_env('CI_MERGE_REQUEST_IID', nil) stub_env('TOP_UPSTREAM_MERGE_REQUEST_IID', nil) stub_env('QA_RUN_TYPE', run_type) diff --git a/qa/spec/tools/ci/non_empty_suites_spec.rb b/qa/spec/tools/ci/non_empty_suites_spec.rb index d9bfe1eebe7..f71e07aa4b4 100644 --- a/qa/spec/tools/ci/non_empty_suites_spec.rb +++ b/qa/spec/tools/ci/non_empty_suites_spec.rb @@ -9,11 +9,11 @@ RSpec.describe QA::Tools::Ci::NonEmptySuites do allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new)) allow(Open3).to receive(:capture3).and_return(["output\n0", "", status]) allow(Open3).to receive(:capture3) - .with("bundle exec bin/qa Test::Instance::All --count-examples-only --address http://dummy1.test") + .with("bundle exec bin/qa QA::Scenario::Test::Instance::All --count-examples-only --address http://dummy1.test") .and_return(["output\n1", "", status]) end it "returns runnable test suites" do - expect(non_empty_suites.fetch).to eq("Test::Instance::All") + expect(non_empty_suites.fetch).to eq("QA::Scenario::Test::Instance::All") end end diff --git a/qa/spec/tools/ci/qa_changes_spec.rb b/qa/spec/tools/ci/qa_changes_spec.rb index bc98ec16d7f..d93d3cd9258 100644 --- a/qa/spec/tools/ci/qa_changes_spec.rb +++ b/qa/spec/tools/ci/qa_changes_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true RSpec.describe QA::Tools::Ci::QaChanges do - subject(:qa_changes) { described_class.new(mr_diff, mr_labels) } + subject(:qa_changes) { described_class.new(mr_diff, mr_labels, additional_group_spec_list) } let(:mr_labels) { [] } + let(:additional_group_spec_list) { [] } before do allow(File).to receive(:directory?).and_return(false) @@ -35,7 +36,7 @@ RSpec.describe QA::Tools::Ci::QaChanges do context "with framework changes" do let(:mr_diff) { [{ path: "qa/qa.rb" }] } - it ".qa_tests do not return specifix specs" do + it ".qa_tests do not return specific specs" do expect(qa_changes.qa_tests).to be_nil end @@ -75,6 +76,43 @@ RSpec.describe QA::Tools::Ci::QaChanges do ) end end + + context "when configured to run tests from other stages" do + let(:additional_group_spec_list) do + { + 'foo' => %w[create], + 'bar' => %w[monitor verify] + } + end + + context "with a single extra stage configured for the group name" do + let(:mr_labels) { %w[devops::manage group::foo] } + + it ".qa_tests return specs for both devops stage and create stage" do + expect(qa_changes.qa_tests.split(" ")).to include( + "qa/specs/features/browser_ui/1_manage/", + "qa/specs/features/api/1_manage/", + "qa/specs/features/browser_ui/3_create/", + "qa/specs/features/api/3_create/" + ) + end + end + + context "with a multiple extra stages configured for the group name" do + let(:mr_labels) { %w[devops::manage group::bar] } + + it ".qa_tests return specs for both devops stage and multiple other stages" do + expect(qa_changes.qa_tests.split(" ")).to include( + "qa/specs/features/browser_ui/1_manage/", + "qa/specs/features/api/1_manage/", + "qa/specs/features/browser_ui/8_monitor/", + "qa/specs/features/api/8_monitor/", + "qa/specs/features/browser_ui/4_verify/", + "qa/specs/features/api/4_verify/" + ) + end + end + end end context "with quarantine changes" do @@ -84,4 +122,14 @@ RSpec.describe QA::Tools::Ci::QaChanges do expect(qa_changes.quarantine_changes?).to eq(true) end end + + %w[GITALY_SERVER_VERSION Gemfile.lock yarn.lock Dockerfile.assets].each do |dependency_file| + context "when #{dependency_file} change" do + let(:mr_diff) { [{ path: dependency_file }] } + + it ".qa_tests do not return specific specs" do + expect(qa_changes.qa_tests).to be_nil + end + end + end end diff --git a/qa/spec/tools/reliable_report_spec.rb b/qa/spec/tools/reliable_report_spec.rb index d5c898779b8..f08af8a717a 100644 --- a/qa/spec/tools/reliable_report_spec.rb +++ b/qa/spec/tools/reliable_report_spec.rb @@ -145,7 +145,8 @@ describe QA::Tools::ReliableReport do let(:common_api_args) do { verify_ssl: false, - headers: { "PRIVATE-TOKEN" => "gitlab_token" } + headers: { "PRIVATE-TOKEN" => "gitlab_token" }, + cookies: {} } end diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake index 44a794d9f94..435fe8ebb77 100644 --- a/qa/tasks/ci.rake +++ b/qa/tasks/ci.rake @@ -13,8 +13,10 @@ namespace :ci do diff = mr_diff labels = mr_labels + # Assign mapping of groups to tests in stages other than the groups defined stage + additional_group_spec_list = { 'gitaly' => %w[create] } - qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels) + qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels, additional_group_spec_list) logger = qa_changes.logger logger.info("Analyzing merge request changes") |