summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rw-r--r--qa/Dockerfile46
-rw-r--r--qa/Gemfile7
-rw-r--r--qa/Gemfile.lock98
-rw-r--r--qa/Rakefile8
-rw-r--r--qa/qa.rb16
-rw-r--r--qa/qa/fixtures/export.tar.gzbin0 -> 306887 bytes
-rw-r--r--qa/qa/page/admin/overview/users/show.rb30
-rw-r--r--qa/qa/page/base.rb6
-rw-r--r--qa/qa/page/component/import/gitlab.rb40
-rw-r--r--qa/qa/page/component/import/selection.rb23
-rw-r--r--qa/qa/page/component/invite_members_modal.rb6
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb16
-rw-r--r--qa/qa/page/dashboard/snippet/edit.rb3
-rw-r--r--qa/qa/page/group/bulk_import.rb5
-rw-r--r--qa/qa/page/group/menu.rb2
-rw-r--r--qa/qa/page/group/new.rb6
-rw-r--r--qa/qa/page/group/settings/general.rb2
-rw-r--r--qa/qa/page/group/sub_menus/common.rb4
-rw-r--r--qa/qa/page/main/login.rb2
-rw-r--r--qa/qa/page/project/fork/new.rb8
-rw-r--r--qa/qa/page/project/import/github.rb30
-rw-r--r--qa/qa/page/project/import/repo_by_url.rb60
-rw-r--r--qa/qa/page/project/infrastructure/kubernetes/show.rb43
-rw-r--r--qa/qa/page/project/new.rb15
-rw-r--r--qa/qa/page/project/pipeline_editor/show.rb45
-rw-r--r--qa/qa/page/project/settings/advanced.rb2
-rw-r--r--qa/qa/page/project/settings/default_branch.rb29
-rw-r--r--qa/qa/page/project/settings/deploy_tokens.rb4
-rw-r--r--qa/qa/page/project/settings/mirroring_repositories.rb11
-rw-r--r--qa/qa/page/project/settings/repository.rb8
-rw-r--r--qa/qa/page/project/sub_menus/ci_cd.rb18
-rw-r--r--qa/qa/page/project/sub_menus/project.rb6
-rw-r--r--qa/qa/resource/base.rb218
-rw-r--r--qa/qa/resource/deploy_token.rb2
-rw-r--r--qa/qa/resource/group.rb4
-rw-r--r--qa/qa/resource/group_base.rb10
-rw-r--r--qa/qa/resource/import_project.rb35
-rw-r--r--qa/qa/resource/kubernetes_cluster/project_cluster.rb17
-rw-r--r--qa/qa/resource/merge_request.rb70
-rw-r--r--qa/qa/resource/package.rb7
-rw-r--r--qa/qa/resource/personal_access_token.rb24
-rw-r--r--qa/qa/resource/personal_access_token_cache.rb17
-rw-r--r--qa/qa/resource/project.rb32
-rw-r--r--qa/qa/resource/project_imported_from_github.rb65
-rw-r--r--qa/qa/resource/project_imported_from_url.rb27
-rw-r--r--qa/qa/resource/repository/wiki_push.rb1
-rw-r--r--qa/qa/resource/user.rb2
-rw-r--r--qa/qa/runtime/allure_report.rb38
-rw-r--r--qa/qa/runtime/browser.rb80
-rw-r--r--qa/qa/runtime/env.rb25
-rw-r--r--qa/qa/scenario/test/integration/registry.rb13
-rw-r--r--qa/qa/service/cluster_provider/k3s_cilium.rb93
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb16
-rw-r--r--qa/qa/service/docker_run/k3s.rb15
-rw-r--r--qa/qa/service/kubernetes_cluster.rb24
-rw-r--r--qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb133
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb8
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb4
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb2
-rw-r--r--qa/qa/specs/features/api/5_package/container_registry_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb48
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb87
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb13
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb90
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/container_registry_omnibus_spec.rb88
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb279
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/online_garbage_collection_spec.rb108
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb4
-rw-r--r--qa/qa/specs/helpers/context_selector.rb16
-rw-r--r--qa/qa/specs/runner.rb2
-rw-r--r--qa/spec/runtime/env_spec.rb6
-rw-r--r--qa/spec/service/docker_run/gitlab_runner_spec.rb2
-rw-r--r--qa/spec/specs/helpers/context_selector_spec.rb251
-rw-r--r--qa/tasks/webdrivers.rake4
92 files changed, 1974 insertions, 667 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 81f207cbab9..ad4bffb3bf2 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -3,10 +3,6 @@ LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive"
ENV DOCKER_VERSION="17.09.0-ce"
-ENV CHROME_VERSION="87.0.4280.141-1"
-ENV CHROME_DRIVER_VERSION="87.0.4280.88"
-ENV CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb"
-ENV CHROME_URL="https://s3.amazonaws.com/gitlab-google-chrome-stable/${CHROME_DEB}"
##
# Update APT sources and install dependencies
@@ -22,22 +18,6 @@ RUN wget -q "https://download.docker.com/linux/static/stable/x86_64/docker-${DOC
rm "docker-${DOCKER_VERSION}.tgz"
##
-# Install Google Chrome version with headless support
-# Download from our local S3 bucket, populated by https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/scripts/cache-google-chrome
-#
-RUN curl --silent --show-error --fail -O "${CHROME_URL}" && \
- dpkg -i "./${CHROME_DEB}" || true && \
- apt-get install -f -y && \
- rm -f "./${CHROME_DEB}"
-
-##
-# Install chromedriver to make it work with Selenium
-#
-RUN wget -q "https://chromedriver.storage.googleapis.com/${CHROME_DRIVER_VERSION}/chromedriver_linux64.zip"
-RUN unzip chromedriver_linux64.zip -d /usr/local/bin
-RUN rm -f chromedriver_linux64.zip
-
-##
# Install client certificate - Bug in Chrome Headless: https://gitlab.com/gitlab-org/gitlab/-/issues/331492
#
# RUN apt install -y libnss3-tools
@@ -66,8 +46,32 @@ RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
apt-get update -y && apt-get install google-cloud-sdk kubectl -y
+##
+# Install Google Chrome version with headless support
+# Download from our local S3 bucket, populated by https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/scripts/cache-google-chrome
+#
+# https://s3.amazonaws.com/gitlab-google-chrome-stable
+ENV CHROME_VERSION="91.0.4472.77-1"
+ENV CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb"
+ENV CHROME_URL="https://s3.amazonaws.com/gitlab-google-chrome-stable/${CHROME_DEB}"
+RUN curl --silent --show-error --fail -O "${CHROME_URL}" && \
+ dpkg -i "./${CHROME_DEB}" || true && \
+ apt-get install -f -y && \
+ rm -f "./${CHROME_DEB}"
+
WORKDIR /home/gitlab/qa
+
+# Install qa dependencies or fetch from cache if unchanged
COPY ./qa/Gemfile* /home/gitlab/qa/
+RUN bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
+
+##
+# Fetch chromedriver based on version of chrome
+# Copy rakefile first so that webdriver is not reinstalled on every code change
+# https://github.com/titusfortner/webdrivers
+COPY ./qa/tasks/webdrivers.rake /home/gitlab/qa/tasks/
+RUN bundle exec rake -f tasks/webdrivers.rake webdrivers:chromedriver:update
+
COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/
# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS
# The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS)
@@ -75,7 +79,7 @@ COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
COPY ./lib/gitlab.rb /home/gitlab/lib/
COPY ./lib/gitlab/utils.rb /home/gitlab/lib/gitlab/
COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
-RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
+
COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
diff --git a/qa/Gemfile b/qa/Gemfile
index 0cb53ca99dc..ff2074b6191 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -9,7 +9,7 @@ gem 'capybara', '~> 3.29.0'
gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.3'
gem 'rspec', '~> 3.7'
-gem 'selenium-webdriver', '~> 3.12'
+gem 'selenium-webdriver', '~> 4.0.0.beta4'
gem 'airborne', '~> 0.3.4'
gem 'rest-client', '~> 2.1.0'
gem 'nokogiri', '~> 1.11.1'
@@ -22,9 +22,10 @@ gem 'rotp', '~> 3.1.0'
gem 'timecop', '~> 0.9.1'
gem 'parallel', '~> 1.19'
gem 'rspec-parameterized', '~> 0.4.2'
-gem 'github_api', '~> 0.18.2'
+gem "octokit", "~> 4.21"
+gem "webdrivers", "~> 4.6"
-gem 'chemlab', '~> 0.5'
+gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
group :development do
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 8b6c4ca0d3e..47dd5ac118e 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -19,10 +19,10 @@ GEM
rack-test (>= 1.1.0, < 2.0)
rest-client (>= 2.0.2, < 3.0)
rspec (~> 3.8)
- allure-rspec (2.14.1)
- allure-ruby-commons (= 2.14.1)
+ allure-rspec (2.14.2)
+ allure-ruby-commons (= 2.14.2)
rspec-core (>= 3.8, < 4)
- allure-ruby-commons (2.14.1)
+ allure-ruby-commons (2.14.2)
mime-types (>= 3.3, < 4)
oj (>= 3.10, < 4)
require_all (>= 2, < 4)
@@ -41,43 +41,51 @@ GEM
capybara-screenshot (1.0.23)
capybara (>= 1.0, < 4)
launchy
- chemlab (0.5.0)
- rake (~> 12.3.0)
- selenium-webdriver (~> 3.12)
- watir (~> 6.17)
+ chemlab (0.7.2)
+ colorize (~> 0.8)
+ i18n (~> 1.8)
+ rake (>= 12, < 14)
+ selenium-webdriver (>= 3, < 5)
+ watir (>= 6, < 8)
chemlab-library-www-gitlab-com (0.1.1)
chemlab (~> 0.4)
- childprocess (3.0.0)
+ 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.8)
- descendants_tracker (0.0.4)
- thread_safe (~> 0.3, >= 0.3.1)
+ concurrent-ruby (1.1.9)
diff-lcs (1.3)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
equalizer (0.0.11)
faker (1.9.3)
i18n (>= 0.7)
- faraday (0.17.3)
+ 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)
- github_api (0.18.2)
- addressable (~> 2.4)
- descendants_tracker (~> 0.0.4)
- faraday (~> 0.8)
- hashie (~> 3.5, >= 3.5.2)
- oauth2 (~> 1.0)
+ 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)
gitlab-qa (4.0.0)
- hashie (3.6.0)
http-accept (1.7.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
- jwt (2.2.2)
knapsack (1.17.1)
rake
launchy (2.4.3)
@@ -89,24 +97,19 @@ GEM
method_source (0.9.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
- mime-types-data (3.2020.0425)
+ mime-types-data (3.2021.0704)
mini_mime (1.0.2)
mini_portile2 (2.5.0)
minitest (5.14.4)
- multi_json (1.15.0)
- multi_xml (0.6.0)
multipart-post (2.1.1)
netrc (0.11.0)
nokogiri (1.11.1)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
- oauth2 (1.4.4)
- faraday (>= 0.8, < 2.0)
- jwt (>= 1.0, < 3.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- oj (3.11.5)
+ octokit (4.21.0)
+ faraday (>= 0.9)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ oj (3.12.1)
parallel (1.19.2)
parallel_tests (2.29.0)
parallel
@@ -129,19 +132,20 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (12.3.3)
- regexp_parser (1.6.0)
+ regexp_parser (1.8.2)
require_all (3.0.0)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
+ rexml (3.2.5)
rotp (3.1.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
- rspec-core (3.9.2)
+ rspec-core (3.9.3)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
@@ -157,14 +161,19 @@ GEM
unparser
rspec-retry (0.6.1)
rspec-core (> 3.3)
- rspec-support (3.9.3)
+ rspec-support (3.9.4)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
ruby-debug-ide (0.7.2)
rake (>= 0.8.1)
- rubyzip (1.3.0)
- selenium-webdriver (3.142.6)
- childprocess (>= 0.5, < 4.0)
+ ruby2_keywords (0.0.4)
+ rubyzip (2.3.2)
+ sawyer (0.8.2)
+ addressable (>= 2.3.5)
+ faraday (> 0.8, < 2.0)
+ selenium-webdriver (4.0.0.beta4)
+ childprocess (>= 0.5, < 5.0)
+ rexml (~> 3.2)
rubyzip (>= 1.2.2)
systemu (2.6.5)
thread_safe (0.3.6)
@@ -184,9 +193,13 @@ GEM
procto (~> 0.0.2)
uuid (2.3.9)
macaddr (~> 1.0)
- watir (6.18.0)
+ watir (6.19.1)
regexp_parser (>= 1.2, < 3)
- selenium-webdriver (>= 3.8)
+ selenium-webdriver (>= 3.142.7)
+ webdrivers (4.6.0)
+ nokogiri (~> 1.6)
+ rubyzip (>= 1.3.0)
+ selenium-webdriver (>= 3.0, < 4.0)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.4.2)
@@ -200,13 +213,13 @@ DEPENDENCIES
allure-rspec (~> 2.14.1)
capybara (~> 3.29.0)
capybara-screenshot (~> 1.0.23)
- chemlab (~> 0.5)
+ chemlab (~> 0.7)
chemlab-library-www-gitlab-com (~> 0.1)
faker (~> 1.6, >= 1.6.6)
- github_api (~> 0.18.2)
gitlab-qa
knapsack (~> 1.17)
nokogiri (~> 1.11.1)
+ octokit (~> 4.21)
parallel (~> 1.19)
parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1)
@@ -218,8 +231,9 @@ DEPENDENCIES
rspec-retry (~> 0.6.1)
rspec_junit_formatter (~> 0.4.1)
ruby-debug-ide (~> 0.7.0)
- selenium-webdriver (~> 3.12)
+ selenium-webdriver (~> 4.0.0.beta4)
timecop (~> 0.9.1)
+ webdrivers (~> 4.6)
BUNDLED WITH
- 2.1.4
+ 2.2.22
diff --git a/qa/Rakefile b/qa/Rakefile
index 0a040f0aee7..f24c81a9ec2 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+# rubocop:disable Rails/RakeEnvironment
+
+load 'tasks/webdrivers.rake'
require_relative 'qa/tools/revoke_all_personal_access_tokens'
require_relative 'qa/tools/delete_subgroups'
@@ -34,7 +37,9 @@ task :run_artillery_load_tests do
urls_file = ENV['URLS_FILE_PATH'] || 'urls.yml'
unless File.exist?(urls_file)
- raise "\n#{urls_file} file is missing. Please provide correct URLS_FILE_PATH or all of HOST_URL, LARGE_ISSUE_URL and LARGE_MR_URL\n\n"
+ raise(<<~ERR)
+ #{urls_file} file is missing. Please provide correct URLS_FILE_PATH or all of HOST_URL, LARGE_ISSUE_URL and LARGE_MR_URL\n
+ ERR
end
urls = YAML.safe_load(File.read(urls_file))
@@ -59,3 +64,4 @@ desc "Deletes projects directly under the provided group"
task :delete_projects do
QA::Tools::DeleteProjects.new.run
end
+# rubocop:enable Rails/RakeEnvironment
diff --git a/qa/qa.rb b/qa/qa.rb
index aad40666065..566effb3cd9 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -79,6 +79,7 @@ module QA
autoload :GroupLabel, 'qa/resource/group_label'
autoload :MergeRequest, 'qa/resource/merge_request'
autoload :ProjectImportedFromGithub, 'qa/resource/project_imported_from_github'
+ autoload :ProjectImportedFromURL, 'qa/resource/project_imported_from_url'
autoload :MergeRequestFromFork, 'qa/resource/merge_request_from_fork'
autoload :DeployKey, 'qa/resource/deploy_key'
autoload :DeployToken, 'qa/resource/deploy_token'
@@ -87,6 +88,7 @@ module QA
autoload :CiVariable, 'qa/resource/ci_variable'
autoload :Runner, 'qa/resource/runner'
autoload :PersonalAccessToken, 'qa/resource/personal_access_token'
+ autoload :PersonalAccessTokenCache, 'qa/resource/personal_access_token_cache'
autoload :ProjectAccessToken, 'qa/resource/project_access_token'
autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone'
@@ -106,6 +108,7 @@ module QA
autoload :RegistryRepository, 'qa/resource/registry_repository'
autoload :Package, 'qa/resource/package'
autoload :PipelineSchedules, 'qa/resource/pipeline_schedules'
+ autoload :ImportProject, 'qa/resource/import_project'
module KubernetesCluster
autoload :Base, 'qa/resource/kubernetes_cluster/base'
@@ -169,6 +172,7 @@ module QA
autoload :ObjectStorage, 'qa/scenario/test/integration/object_storage'
autoload :SMTP, 'qa/scenario/test/integration/smtp'
autoload :SSHTunnel, 'qa/scenario/test/integration/ssh_tunnel'
+ autoload :Registry, 'qa/scenario/test/integration/registry'
end
module Sanity
@@ -287,6 +291,7 @@ module QA
module Import
autoload :Github, 'qa/page/project/import/github'
+ autoload :RepoByURL, 'qa/page/project/import/repo_by_url'
end
module Pipeline
@@ -295,6 +300,10 @@ module QA
autoload :New, 'qa/page/project/pipeline/new'
end
+ module PipelineEditor
+ autoload :Show, 'qa/page/project/pipeline_editor/show'
+ end
+
module Tag
autoload :Index, 'qa/page/project/tag/index'
autoload :New, 'qa/page/project/tag/new'
@@ -330,6 +339,7 @@ module QA
autoload :MergeRequest, 'qa/page/project/settings/merge_request'
autoload :MirroringRepositories, 'qa/page/project/settings/mirroring_repositories'
autoload :ProtectedTags, 'qa/page/project/settings/protected_tags'
+ autoload :DefaultBranch, 'qa/page/project/settings/default_branch'
autoload :VisibilityFeaturesPermissions, 'qa/page/project/settings/visibility_features_permissions'
autoload :AccessTokens, 'qa/page/project/settings/access_tokens'
@@ -529,6 +539,11 @@ module QA
autoload :CommitModal, 'qa/page/component/commit_modal'
autoload :VisibilitySetting, 'qa/page/component/visibility_setting'
+ module Import
+ autoload :Gitlab, 'qa/page/component/import/gitlab'
+ autoload :Selection, 'qa/page/component/import/selection'
+ end
+
module Issuable
autoload :Common, 'qa/page/component/issuable/common'
autoload :Sidebar, 'qa/page/component/issuable/sidebar'
@@ -585,6 +600,7 @@ module QA
autoload :Minikube, 'qa/service/cluster_provider/minikube'
autoload :K3d, 'qa/service/cluster_provider/k3d'
autoload :K3s, 'qa/service/cluster_provider/k3s'
+ autoload :K3sCilium, 'qa/service/cluster_provider/k3s_cilium'
end
module DockerRun
diff --git a/qa/qa/fixtures/export.tar.gz b/qa/qa/fixtures/export.tar.gz
new file mode 100644
index 00000000000..08e4f0c9c43
--- /dev/null
+++ b/qa/qa/fixtures/export.tar.gz
Binary files differ
diff --git a/qa/qa/page/admin/overview/users/show.rb b/qa/qa/page/admin/overview/users/show.rb
index f455bd31d14..be73f3d80bf 100644
--- a/qa/qa/page/admin/overview/users/show.rb
+++ b/qa/qa/page/admin/overview/users/show.rb
@@ -11,12 +11,25 @@ module QA
end
view 'app/views/admin/users/show.html.haml' do
- element :confirm_user_button
element :user_id_content
end
- view 'app/views/admin/users/_approve_user.html.haml' do
+ view 'app/assets/javascripts/admin/users/components/actions/approve.vue' do
element :approve_user_button
+ element :approve_user_confirm_button
+ end
+
+ view 'app/assets/javascripts/admin/users/components/user_actions.vue' do
+ element :user_actions_dropdown_toggle
+ end
+
+ view 'app/helpers/users_helper.rb' do
+ element :confirm_user_button
+ element :confirm_user_confirm_button
+ end
+
+ def open_user_actions_dropdown(user)
+ click_element(:user_actions_dropdown_toggle, username: user.username)
end
def click_impersonate_user
@@ -28,15 +41,14 @@ module QA
end
def confirm_user
- accept_confirm do
- click_element :confirm_user_button
- end
+ click_element :confirm_user_button
+ click_element :confirm_user_confirm_button
end
- def approve_user
- accept_confirm do
- click_element :approve_user_button
- end
+ def approve_user(user)
+ open_user_actions_dropdown(user)
+ click_element :approve_user_button
+ click_element :approve_user_confirm_button
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 66040bb9849..6b54d8ab1ac 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -104,7 +104,8 @@ module QA
end
def find_element(name, **kwargs)
- wait_for_requests
+ skip_finished_loading_check = kwargs.delete(:skip_finished_loading_check)
+ wait_for_requests(skip_finished_loading_check: skip_finished_loading_check)
element_selector = element_selector_css(name, reject_capybara_query_keywords(kwargs))
find(element_selector, only_capybara_query_keywords(kwargs))
@@ -196,7 +197,8 @@ module QA
# replace with (..., page = self.class)
def click_element(name, page = nil, **kwargs)
- wait_for_requests
+ skip_finished_loading_check = kwargs.delete(:skip_finished_loading_check)
+ wait_for_requests(skip_finished_loading_check: skip_finished_loading_check)
wait = kwargs.delete(:wait) || Capybara.default_max_wait_time
text = kwargs.delete(:text)
diff --git a/qa/qa/page/component/import/gitlab.rb b/qa/qa/page/component/import/gitlab.rb
new file mode 100644
index 00000000000..2fd2a45b399
--- /dev/null
+++ b/qa/qa/page/component/import/gitlab.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Import
+ module Gitlab
+ def self.included(base)
+ super
+
+ base.view 'app/views/import/gitlab_projects/new.html.haml' do
+ element :import_project_button
+ end
+
+ base.view 'app/views/import/shared/_new_project_form.html.haml' do
+ element :project_name_field
+ element :project_slug_field
+ end
+ end
+
+ def set_imported_project_name(name)
+ fill_element(:project_name_field, name)
+ end
+
+ def attach_exported_file(path)
+ page.attach_file("file", path, make_visible: { display: 'block' })
+ end
+
+ def click_import_gitlab_project
+ click_element(:import_project_button)
+
+ wait_until(reload: false) do
+ has_notice?("The project was successfully imported.")
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/import/selection.rb b/qa/qa/page/component/import/selection.rb
new file mode 100644
index 00000000000..6cacdd84f13
--- /dev/null
+++ b/qa/qa/page/component/import/selection.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Import
+ module Selection
+ def self.included(base)
+ super
+
+ base.view 'app/views/projects/_import_project_pane.html.haml' do
+ element :gitlab_import_button
+ end
+ end
+
+ def click_gitlab
+ click_element(:gitlab_import_button)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb
index 9883ef22029..7cec4588af5 100644
--- a/qa/qa/page/component/invite_members_modal.rb
+++ b/qa/qa/page/component/invite_members_modal.rb
@@ -19,6 +19,10 @@ module QA
element :group_select_dropdown_search_field
end
+ base.view 'app/assets/javascripts/invite_members/components/members_token_select.vue' do
+ element :members_token_select_input
+ end
+
base.view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do
element :invite_a_group_button
end
@@ -42,7 +46,7 @@ module QA
within_element(:invite_members_modal_content) do
fill_element :access_level_dropdown, with: access_level
- fill_in 'Select members or type email addresses', with: username
+ fill_element :members_token_select_input, username
Support::WaitForRequests.wait_for_requests
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index 3a9d316c321..971e7634f6d 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -40,16 +40,22 @@ module QA
base.view 'app/views/shared/issuable/_sidebar.html.haml' do
element :assignee_block
- element :edit_milestone_link
element :milestone_block
- element :milestone_link
+ end
+
+ base.view 'app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue' do
+ element :milestone_link, 'data-qa-selector="`${issuableAttribute}_link`"' # rubocop:disable QA/ElementWithPattern
+ end
+
+ base.view 'app/assets/javascripts/sidebar/components/sidebar_editable_item.vue' do
+ element :edit_link
end
end
def assign_milestone(milestone)
- click_element(:edit_milestone_link)
within_element(:milestone_block) do
- click_link("#{milestone.title}")
+ click_element(:edit_link)
+ click_on(milestone.title)
end
wait_until(reload: false) do
@@ -89,7 +95,7 @@ module QA
def has_milestone?(milestone_title)
wait_milestone_block_finish_loading do
- has_element?(:milestone_link, title: milestone_title)
+ has_element?(:milestone_link, text: milestone_title)
end
end
diff --git a/qa/qa/page/dashboard/snippet/edit.rb b/qa/qa/page/dashboard/snippet/edit.rb
index cd8ac77ae04..939413f6d76 100644
--- a/qa/qa/page/dashboard/snippet/edit.rb
+++ b/qa/qa/page/dashboard/snippet/edit.rb
@@ -23,7 +23,8 @@ module QA
end
def add_to_file_content(content)
- text_area.set content
+ text_area.click
+ text_area.send_keys(:home, content) # starts in the beginning of the line
text_area.has_text?(content) # wait for changes to take effect
end
diff --git a/qa/qa/page/group/bulk_import.rb b/qa/qa/page/group/bulk_import.rb
index 11741bdf4cb..a0511c9a16c 100644
--- a/qa/qa/page/group/bulk_import.rb
+++ b/qa/qa/page/group/bulk_import.rb
@@ -10,12 +10,15 @@ module QA
view "app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue" do
element :import_item
- element :target_namespace_selector_dropdown
element :target_group_dropdown_item
element :import_status_indicator
element :import_group_button
end
+ view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
+ element :target_namespace_selector_dropdown
+ end
+
# Import source group in to target group
#
# @param [String] source_group_name
diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb
index 9a3b129b6d2..338a135614d 100644
--- a/qa/qa/page/group/menu.rb
+++ b/qa/qa/page/group/menu.rb
@@ -6,7 +6,7 @@ module QA
class Menu < Page::Base
include SubMenus::Common
- view 'app/views/layouts/nav/sidebar/_group.html.haml' do
+ view 'app/views/layouts/nav/sidebar/_group_menus.html.haml' do
element :general_settings_link
element :group_issues_item
element :group_members_item
diff --git a/qa/qa/page/group/new.rb b/qa/qa/page/group/new.rb
index 66afcf7547c..c24712b9418 100644
--- a/qa/qa/page/group/new.rb
+++ b/qa/qa/page/group/new.rb
@@ -21,6 +21,10 @@ module QA
element :connect_instance_button
end
+ view 'app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue' do
+ element :panel_link
+ end
+
def set_path(path)
fill_element(:group_path_field, path)
fill_element(:group_name_field, path)
@@ -62,7 +66,7 @@ module QA
end
def switch_to_import_tab
- click_element("import-group-pane_link")
+ click_element(:panel_link, panel_name: 'import-group-pane')
end
end
end
diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb
index 4977e5c7105..2e7ab131225 100644
--- a/qa/qa/page/group/settings/general.rb
+++ b/qa/qa/page/group/settings/general.rb
@@ -38,7 +38,7 @@ module QA
element :project_creation_level_dropdown
end
- view 'app/views/groups/settings/_advanced.html.haml' do
+ view 'app/views/groups/settings/_transfer.html.haml' do
element :select_group_dropdown
element :transfer_group_button
end
diff --git a/qa/qa/page/group/sub_menus/common.rb b/qa/qa/page/group/sub_menus/common.rb
index 86102f70d29..2f8a3fdeb4e 100644
--- a/qa/qa/page/group/sub_menus/common.rb
+++ b/qa/qa/page/group/sub_menus/common.rb
@@ -12,8 +12,8 @@ module QA
super
base.class_eval do
- view 'app/views/layouts/nav/sidebar/_group.html.haml' do
- element :group_sidebar
+ view 'app/views/shared/nav/_sidebar.html.haml' do
+ element :group_sidebar, 'qa_selector: sidebar_qa_selector(sidebar.container)' # rubocop:disable QA/ElementWithPattern
end
end
end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 3b3057a9acb..2c7ce69e4e5 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -67,7 +67,7 @@ module QA
end
def sign_in_using_admin_credentials
- admin = QA::Resource::User.new.tap do |user|
+ admin = QA::Resource::User.init do |user|
user.username = QA::Runtime::User.admin_username
user.password = QA::Runtime::User.admin_password
end
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
index 5a08f6a3cbd..7062702679a 100644
--- a/qa/qa/page/project/fork/new.rb
+++ b/qa/qa/page/project/fork/new.rb
@@ -9,10 +9,6 @@ module QA
element :fork_namespace_button
end
- view 'app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue' do
- element :fork_groups_list_search_field
- end
-
view 'app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue' do
element :fork_namespace_dropdown
element :fork_project_button
@@ -27,8 +23,8 @@ module QA
end
end
- def search_for_group(group_name)
- find_element(:fork_groups_list_search_field).set(group_name)
+ def fork_namespace_dropdown_values
+ find_element(:fork_namespace_dropdown).all(:option).map { |option| option.text.tr("\n", '').strip }
end
end
end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index dc683f7314b..74bc4cec467 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -14,13 +14,16 @@ module QA
view 'app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue' do
element :project_import_row
- element :project_namespace_select
element :project_path_field
element :import_button
element :project_path_content
element :go_to_project_button
end
+ view "app/assets/javascripts/import_entities/components/group_dropdown.vue" do
+ element :target_namespace_selector_dropdown
+ end
+
def add_personal_access_token(personal_access_token)
# If for some reasons this process is retried, user cannot re-enter github token in the same group
# In this case skip this step and proceed to import project row
@@ -37,9 +40,18 @@ module QA
choose_test_namespace(full_path)
set_path(full_path, name)
import_project(full_path)
+
wait_for_success
end
+ # TODO: refactor to use 'go to project' button instead of generic main menu
+ def go_to_project(name)
+ Page::Main::Menu.perform(&:go_to_projects)
+ Page::Dashboard::Projects.perform do |dashboard|
+ dashboard.go_to_project(name)
+ end
+ end
+
private
def within_repo_path(full_path, &block)
@@ -50,10 +62,9 @@ module QA
def choose_test_namespace(full_path)
within_repo_path(full_path) do
- click_element :project_namespace_select
+ within_element(:target_namespace_selector_dropdown) { click_button(class: 'dropdown-toggle') }
+ click_element(:target_group_dropdown_item, group_name: Runtime::Namespace.path)
end
-
- search_and_select(Runtime::Namespace.path)
end
def set_path(full_path, name)
@@ -77,14 +88,9 @@ module QA
reload: true,
skip_finished_loading_check_on_refresh: true
) do
- page.has_no_content?('Importing 1 repository')
- end
- end
-
- def go_to_project(name)
- Page::Main::Menu.perform(&:go_to_projects)
- Page::Dashboard::Projects.perform do |dashboard|
- dashboard.go_to_project(name)
+ # TODO: Refactor to explicitly wait for specific project import successful status
+ # This check can create false positive if main importing message appears with delay and check exits early
+ page.has_no_content?('Importing 1 repository', wait: 3)
end
end
diff --git a/qa/qa/page/project/import/repo_by_url.rb b/qa/qa/page/project/import/repo_by_url.rb
new file mode 100644
index 00000000000..0e7524a181a
--- /dev/null
+++ b/qa/qa/page/project/import/repo_by_url.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Import
+ class RepoByURL < Page::Base
+ include Page::Component::Select2
+
+ view 'app/views/projects/_new_project_fields.html.haml' do
+ element :select_namespace_dropdown
+ end
+
+ def import!(gitlab_repo_path, name)
+ fill_git_repository_url_link(gitlab_repo_path)
+ fill_project_name(name)
+ choose_test_namespace
+ click_create_button
+
+ wait_for_success
+
+ go_to_project(name)
+ end
+
+ private
+
+ def fill_git_repository_url_link(gitlab_repo_path)
+ fill_in 'project_import_url', with: gitlab_repo_path
+ end
+
+ def fill_project_name(name)
+ fill_in 'project_name', with: name
+ end
+
+ def choose_test_namespace
+ find('.js-select-namespace').click
+ search_and_select(Runtime::Namespace.path)
+ end
+
+ def click_create_button
+ find('.btn-confirm').click
+ end
+
+ def wait_for_success
+ wait_until(max_duration: 60, sleep_interval: 5.0, reload: true, skip_finished_loading_check_on_refresh: true) do
+ page.has_no_content?('Import in progress', wait: 3.0)
+ end
+ end
+
+ def go_to_project(name)
+ Page::Main::Menu.perform(&:go_to_projects)
+ Page::Dashboard::Projects.perform do |dashboard|
+ dashboard.go_to_project(name)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/infrastructure/kubernetes/show.rb b/qa/qa/page/project/infrastructure/kubernetes/show.rb
index e3b4f8fe4d9..62a04a53a2f 100644
--- a/qa/qa/page/project/infrastructure/kubernetes/show.rb
+++ b/qa/qa/page/project/infrastructure/kubernetes/show.rb
@@ -6,10 +6,6 @@ module QA
module Infrastructure
module Kubernetes
class Show < Page::Base
- view 'app/assets/javascripts/clusters/components/applications.vue' do
- element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/assets/javascripts/clusters/forms/components/integration_form.vue' do
element :integration_status_toggle, required: true
element :base_domain_field, required: true
@@ -20,15 +16,6 @@ module QA
element :details, required: true
end
- view 'app/views/clusters/clusters/_applications_tab.html.haml' do
- element :applications, required: true
- end
-
- view 'app/assets/javascripts/clusters/components/application_row.vue' do
- element :install_button
- element :uninstall_button
- end
-
view 'app/views/clusters/clusters/_health.html.haml' do
element :cluster_health_section
end
@@ -42,36 +29,6 @@ module QA
click_element :details
end
- def open_applications
- has_element?(:applications, wait: 30)
- click_element :applications
- end
-
- def install!(application_name)
- within_element(application_name) do
- has_element?(:install_button, application: application_name, wait: 30)
- click_element :install_button
- end
- end
-
- def await_installed(application_name)
- within_element(application_name) do
- has_element?(:uninstall_button, application: application_name, wait: 300, skip_finished_loading_check: true)
- end
- end
-
- def has_application_installed?(application_name)
- within_element(application_name) do
- has_element?(:uninstall_button, application: application_name, wait: 300)
- end
- end
-
- def ingress_ip
- # We need to wait longer since it can take some time before the
- # ip address is assigned for the ingress controller
- page.find('#ingress-endpoint', wait: 1200).value
- end
-
def set_domain(domain)
fill_element :base_domain_field, domain
end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index b14afa90442..170cc14b27f 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -8,6 +8,10 @@ module QA
include Page::Component::Select2
include Page::Component::VisibilitySetting
+ include Layout::Flash
+ include Page::Component::Import::Selection
+ include Page::Component::Import::Gitlab
+
view 'app/views/projects/_new_project_fields.html.haml' do
element :initialize_with_readme_checkbox
element :project_namespace_select
@@ -25,16 +29,15 @@ module QA
end
view 'app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue' do
- element :blank_project_link, ':data-qa-selector="`${panel.name}_link`"' # rubocop:disable QA/ElementWithPattern
- element :create_from_template_link, ':data-qa-selector="`${panel.name}_link`"' # rubocop:disable QA/ElementWithPattern
+ element :panel_link
end
def click_blank_project_link
- click_element :blank_project_link
+ click_element(:panel_link, panel_name: 'blank_project')
end
def click_create_from_template_link
- click_element :create_from_template_link
+ click_element(:panel_link, panel_name: 'create_from_template')
end
def choose_test_namespace
@@ -76,6 +79,10 @@ module QA
click_link 'GitHub'
end
+ def click_repo_by_url_link
+ click_button 'Repo by URL'
+ end
+
def enable_initialize_with_readme
check_element(:initialize_with_readme_checkbox)
end
diff --git a/qa/qa/page/project/pipeline_editor/show.rb b/qa/qa/page/project/pipeline_editor/show.rb
new file mode 100644
index 00000000000..38c87c8daa1
--- /dev/null
+++ b/qa/qa/page/project/pipeline_editor/show.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module PipelineEditor
+ class Show < QA::Page::Base
+ view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
+ element :branch_selector_button
+ element :menu_branch_button
+ end
+
+ view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ element :target_branch_field
+ end
+
+ def has_branch_selector_button?
+ has_element? :branch_selector_button
+ end
+
+ def click_branch_selector_button
+ wait_until(reload: false) do
+ has_element?(:branch_selector_button)
+ end
+ click_element(:branch_selector_button, skip_finished_loading_check: true)
+ end
+
+ def select_branch_from_dropdown(branch_to_switch_to)
+ wait_until(reload: false) do
+ has_element?(:menu_branch_button)
+ end
+ click_element(:menu_branch_button, text: branch_to_switch_to, skip_finished_loading_check: true)
+ end
+
+ def target_branch_name
+ wait_until(reload: false) do
+ has_element?(:target_branch_field)
+ end
+ find_element(:target_branch_field, skip_finished_loading_check: true).value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
index 9c4b3a3c1c3..0ba856e8a6e 100644
--- a/qa/qa/page/project/settings/advanced.rb
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -51,7 +51,7 @@ module QA
# Workaround for a failure to search when there are no spaces around the /
# https://gitlab.com/gitlab-org/gitlab/-/issues/218965
- search_and_select(namespace.gsub(/([^\s])\/([^\s])/, '\1 / \2'))
+ search_and_select(namespace.gsub(%r{([^\s])/([^\s])}, '\1 / \2'))
click_element(:transfer_button)
fill_confirmation_text(project_name)
diff --git a/qa/qa/page/project/settings/default_branch.rb b/qa/qa/page/project/settings/default_branch.rb
new file mode 100644
index 00000000000..cc28b37b88f
--- /dev/null
+++ b/qa/qa/page/project/settings/default_branch.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ 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
+ element :default_branch_dropdown
+ end
+
+ def set_default_branch(branch)
+ find('.select2-chosen').click
+ search_and_select(branch)
+ end
+
+ def click_save_changes_button
+ find('.btn-confirm').click
+ end
+ end
+ end
+ end
+ end
+end
+
+QA::Page::Project::Settings::DefaultBranch.prepend_mod_with('Page::Project::Settings::DefaultBranch', namespace: QA)
diff --git a/qa/qa/page/project/settings/deploy_tokens.rb b/qa/qa/page/project/settings/deploy_tokens.rb
index b26cae86d8b..db1f6f68ec6 100644
--- a/qa/qa/page/project/settings/deploy_tokens.rb
+++ b/qa/qa/page/project/settings/deploy_tokens.rb
@@ -9,6 +9,7 @@ module QA
element :deploy_token_name_field
element :deploy_token_expires_at_field
element :deploy_token_read_repository_checkbox
+ element :deploy_token_read_package_registry_checkbox
element :deploy_token_read_registry_checkbox
element :create_deploy_token_button
end
@@ -27,8 +28,9 @@ module QA
fill_element(:deploy_token_expires_at_field, expires_at.to_s + "\n")
end
- def fill_scopes(read_repository:, read_registry:)
+ def fill_scopes(read_repository: false, read_registry: false, read_package_registry: false)
check_element(:deploy_token_read_repository_checkbox) if read_repository
+ check_element(:deploy_token_read_package_registry_checkbox) if read_package_registry
check_element(:deploy_token_read_registry_checkbox) if read_registry
end
diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb
index 5e7b68858c8..582079157f2 100644
--- a/qa/qa/page/project/settings/mirroring_repositories.rb
+++ b/qa/qa/page/project/settings/mirroring_repositories.rb
@@ -48,7 +48,7 @@ module QA
end
def mirror_direction=(value)
- raise ArgumentError, "Mirror direction must be 'Push' or 'Pull'" unless %w(Push Pull).include? value
+ raise ArgumentError, "Mirror direction must be 'Push' or 'Pull'" unless %w[Push Pull].include?(value)
select_element(:mirror_direction, value)
@@ -59,7 +59,9 @@ module QA
end
def authentication_method=(value)
- raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'" unless %w(Password None SSH\ public\ key).include? value
+ unless %w[Password None SSH\ public\ key].include?(value)
+ raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'"
+ end
select_element(:authentication_method, value)
end
@@ -129,4 +131,7 @@ module QA
end
end
-QA::Page::Project::Settings::MirroringRepositories.prepend_mod_with('Page::Project::Settings::MirroringRepositories', namespace: QA)
+QA::Page::Project::Settings::MirroringRepositories.prepend_mod_with( # rubocop:disable Cop/InjectEnterpriseEditionModule
+ 'Page::Project::Settings::MirroringRepositories',
+ namespace: QA
+)
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index a02b3d6a7d6..de5b4f37076 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -56,6 +56,14 @@ module QA
ProtectedTags.perform(&block)
end
end
+
+ def expand_default_branch(&block)
+ within('#default-branch-settings') do
+ find('.btn-default').click do
+ DefaultBranch.perform(&block)
+ end
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/sub_menus/ci_cd.rb b/qa/qa/page/project/sub_menus/ci_cd.rb
index 7cb2fd6c655..c8c90df2c1f 100644
--- a/qa/qa/page/project/sub_menus/ci_cd.rb
+++ b/qa/qa/page/project/sub_menus/ci_cd.rb
@@ -20,6 +20,24 @@ module QA
click_element(:sidebar_menu_link, menu_item: 'CI/CD')
end
end
+
+ def go_to_pipeline_editor
+ hover_ci_cd_pipelines do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Editor')
+ end
+ end
+ end
+
+ private
+
+ def hover_ci_cd_pipelines
+ within_sidebar do
+ find_element(:sidebar_menu_link, menu_item: 'CI/CD').hover
+
+ yield
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/sub_menus/project.rb b/qa/qa/page/project/sub_menus/project.rb
index cd08715bcd8..89d4ed578ed 100644
--- a/qa/qa/page/project/sub_menus/project.rb
+++ b/qa/qa/page/project/sub_menus/project.rb
@@ -12,17 +12,13 @@ module QA
base.class_eval do
include QA::Page::Project::SubMenus::Common
-
- view 'app/views/shared/nav/_scope_menu_body.html.haml' do
- element :project_scope_link
- end
end
end
def click_project
retry_on_exception do
within_sidebar do
- click_element(:project_scope_link)
+ click_element(:sidebar_menu_link, menu_item: 'Project scope')
end
end
end
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index 873ba353051..ca0087cf709 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -1,70 +1,143 @@
# frozen_string_literal: true
-require 'forwardable'
require 'capybara/dsl'
require 'active_support/core_ext/array/extract_options'
module QA
module Resource
class Base
- extend SingleForwardable
include ApiFabricator
extend Capybara::DSL
NoValueError = Class.new(RuntimeError)
- def_delegators :evaluator, :attribute
+ class << self
+ # Initialize new instance of class without fabrication
+ #
+ # @param [Proc] prepare_block
+ def init(&prepare_block)
+ new.tap(&prepare_block)
+ end
- def self.fabricate!(*args, &prepare_block)
- fabricate_via_api!(*args, &prepare_block)
- rescue NotImplementedError
- fabricate_via_browser_ui!(*args, &prepare_block)
- end
+ def fabricate!(*args, &prepare_block)
+ fabricate_via_api!(*args, &prepare_block)
+ rescue NotImplementedError
+ fabricate_via_browser_ui!(*args, &prepare_block)
+ end
- def self.fabricate_via_browser_ui!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ def fabricate_via_browser_ui!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) }
- current_url
+ current_url
+ end
end
- end
- def self.fabricate_via_api!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ def fabricate_via_api!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ raise NotImplementedError unless resource.api_support?
+
+ resource.eager_load_api_client!
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
+ end
+ end
+
+ def remove_via_api!(*args, &prepare_block)
+ options = args.extract_options!
+ resource = options.fetch(:resource) { new }
+ parents = options.fetch(:parents) { [] }
+
+ resource.eager_load_api_client!
+
+ do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
+ log_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
+ end
+ end
- raise NotImplementedError unless resource.api_support?
+ private
- resource.eager_load_api_client!
+ def do_fabricate!(resource:, prepare_block:, parents: [])
+ prepare_block.call(resource) if prepare_block
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! }
+ resource_web_url = yield
+ resource.web_url = resource_web_url
+
+ resource
+ end
+
+ def log_fabrication(method, resource, parents, args)
+ return yield unless Runtime::Env.debug?
+
+ start = Time.now
+ prefix = "==#{'=' * parents.size}>"
+ msg = [prefix]
+ msg << "Built a #{name}"
+ msg << "as a dependency of #{parents.last}" if parents.any?
+ msg << "via #{method}"
+
+ yield.tap do
+ msg << "in #{Time.now - start} seconds"
+ puts msg.join(' ')
+ puts if parents.empty?
+ end
+ end
+
+ # Define custom attribute
+ #
+ # @param [Symbol] name
+ # @return [void]
+ def attribute(name, &block)
+ (@attribute_names ||= []).push(name) # save added attributes
+
+ attr_writer(name)
+
+ define_method(name) do
+ instance_variable_get("@#{name}") || instance_variable_set("@#{name}", populate_attribute(name, block))
+ end
+ end
+
+ # Define multiple custom attributes
+ #
+ # @param [Array] names
+ # @return [void]
+ def attributes(*names)
+ names.each { |name| attribute(name) }
end
end
- def self.remove_via_api!(*args, &prepare_block)
- options = args.extract_options!
- resource = options.fetch(:resource) { new }
- parents = options.fetch(:parents) { [] }
+ # Override api reload! and update custom attributes from api_resource
+ #
+ api_reload = instance_method(:reload!)
+ define_method(:reload!) do
+ api_reload.bind_call(self)
+ return self unless api_resource
- resource.eager_load_api_client!
+ all_attributes.each do |attribute_name|
+ api_value = api_resource[attribute_name]
- do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do
- log_fabrication(:api, resource, parents, args) { resource.remove_via_api! }
+ instance_variable_set("@#{attribute_name}", api_value) if api_value
end
+
+ self
end
+ attribute :web_url
+
def fabricate!(*_args)
raise NotImplementedError
end
def visit!
- Runtime::Logger.debug(%Q[Visiting #{self.class.name} at "#{web_url}"])
+ Runtime::Logger.debug(%(Visiting #{self.class.name} at "#{web_url}"))
# Just in case an async action is not yet complete
Support::WaitForRequests.wait_for_requests
@@ -78,14 +151,12 @@ module QA
Support::WaitForRequests.wait_for_requests
end
- def populate(*attributes)
- attributes.each(&method(:public_send))
+ def populate(*attribute_names)
+ attribute_names.each { |attribute_name| public_send(attribute_name) }
end
- def wait_until(max_duration: 60, sleep_interval: 0.1)
- QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval) do
- yield
- end
+ def wait_until(max_duration: 60, sleep_interval: 0.1, &block)
+ QA::Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval, &block)
end
private
@@ -101,70 +172,27 @@ module QA
def attribute_value(name, block)
api_value = api_resource&.dig(name)
- if api_value && block
- log_having_both_api_result_and_block(name, api_value)
- end
+ log_having_both_api_result_and_block(name, api_value) if api_value && block
api_value || (block && instance_exec(&block))
end
- def log_having_both_api_result_and_block(name, api_value)
- QA::Runtime::Logger.info "<#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored."
+ # Get all defined attributes across all parents
+ #
+ # @return [Array<Symbol>]
+ def all_attributes
+ @all_attributes ||= self.class.ancestors
+ .select { |clazz| clazz <= QA::Resource::Base }
+ .map { |clazz| clazz.instance_variable_get(:@attribute_names) }
+ .flatten
+ .compact
end
- def self.do_fabricate!(resource:, prepare_block:, parents: [])
- prepare_block.call(resource) if prepare_block
-
- resource_web_url = yield
- resource.web_url = resource_web_url
-
- resource
- end
- private_class_method :do_fabricate!
-
- def self.log_fabrication(method, resource, parents, args)
- return yield unless Runtime::Env.debug?
-
- start = Time.now
- prefix = "==#{'=' * parents.size}>"
- msg = [prefix]
- msg << "Built a #{name}"
- msg << "as a dependency of #{parents.last}" if parents.any?
- msg << "via #{method}"
-
- yield.tap do
- msg << "in #{Time.now - start} seconds"
- puts msg.join(' ')
- puts if parents.empty?
- end
- end
- private_class_method :log_fabrication
-
- def self.evaluator
- @evaluator ||= Base::DSL.new(self)
- end
- private_class_method :evaluator
-
- class DSL
- def initialize(base)
- @base = base
- end
-
- def attribute(name, &block)
- @base.module_eval do
- attr_writer(name)
-
- define_method(name) do
- instance_variable_get("@#{name}") ||
- instance_variable_set(
- "@#{name}",
- populate_attribute(name, block))
- end
- end
- end
+ def log_having_both_api_result_and_block(name, api_value)
+ QA::Runtime::Logger.info(<<~MSG.strip)
+ <#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored.
+ MSG
end
-
- attribute :web_url
end
end
end
diff --git a/qa/qa/resource/deploy_token.rb b/qa/qa/resource/deploy_token.rb
index 0ba8dbbf287..cd638ad2f85 100644
--- a/qa/qa/resource/deploy_token.rb
+++ b/qa/qa/resource/deploy_token.rb
@@ -37,7 +37,7 @@ module QA
setting.expand_deploy_tokens do |page|
page.fill_token_name(name)
page.fill_token_expires_at(expires_at)
- page.fill_scopes(read_repository: true, read_registry: false)
+ page.fill_scopes(read_repository: true, read_package_registry: true)
page.add_token
end
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 0f06113f85b..ce85273c3b2 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -3,7 +3,7 @@
module QA
module Resource
class Group < GroupBase
- attr_accessor :description
+ attributes :require_two_factor_authentication, :description
attribute :full_path do
determine_full_path
@@ -15,8 +15,6 @@ module QA
end
end
- attribute :require_two_factor_authentication
-
def initialize
@path = Runtime::Namespace.name
@description = "QA test run at #{Runtime::Namespace.time}"
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index 025d98f50e0..652c6cf7d1e 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -9,17 +9,17 @@ module QA
attr_accessor :path
- attribute :id
- attribute :runners_token
- attribute :name
- attribute :full_path
+ attributes :id,
+ :runners_token,
+ :name,
+ :full_path
# Get group labels
#
# @return [Array<QA::Resource::GroupLabel>]
def labels
parse_body(api_get_from("#{api_get_path}/labels")).map do |label|
- GroupLabel.new.tap do |resource|
+ GroupLabel.init do |resource|
resource.api_client = api_client
resource.group = self
resource.id = label[:id]
diff --git a/qa/qa/resource/import_project.rb b/qa/qa/resource/import_project.rb
new file mode 100644
index 00000000000..105d75285f1
--- /dev/null
+++ b/qa/qa/resource/import_project.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class ImportProject < Resource::Project
+ attr_writer :file_path
+
+ def initialize
+ @name = "ImportedProject-#{SecureRandom.hex(8)}"
+ @file_path = ::File.join('qa', 'fixtures', 'export.tar.gz')
+ end
+
+ def fabricate!
+ self.import = true
+ super
+
+ group.visit!
+
+ Page::Group::Show.perform(&:go_to_new_project)
+
+ Page::Project::New.perform do |new_project|
+ new_project.click_import_project
+ new_project.click_gitlab
+ new_project.set_imported_project_name(@name)
+ new_project.attach_exported_file(@file_path)
+ new_project.click_import_gitlab_project
+ end
+ end
+
+ def fabricate_via_api!
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/kubernetes_cluster/project_cluster.rb b/qa/qa/resource/kubernetes_cluster/project_cluster.rb
index b66a75d978b..b3eba77fc46 100644
--- a/qa/qa/resource/kubernetes_cluster/project_cluster.rb
+++ b/qa/qa/resource/kubernetes_cluster/project_cluster.rb
@@ -3,6 +3,8 @@
module QA
module Resource
module KubernetesCluster
+ # TODO: This resource is currently broken, since one-click apps have been removed.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/333818
class ProjectCluster < Base
attr_writer :cluster,
:install_ingress, :install_prometheus, :install_runner, :domain
@@ -11,8 +13,8 @@ module QA
Resource::Project.fabricate!
end
- attribute :ingress_ip do
- Page::Project::Infrastructure::Kubernetes::Show.perform(&:ingress_ip)
+ def ingress_ip
+ @ingress_ip ||= @cluster.fetch_external_ip_for_ingress
end
def fabricate!
@@ -40,17 +42,6 @@ module QA
# We must wait a few seconds for permissions to be set up correctly for new cluster
sleep 25
- # Open applications tab
- show.open_applications
-
- show.install!(:ingress) if @install_ingress
- show.install!(:prometheus) if @install_prometheus
- show.install!(:runner) if @install_runner
-
- show.await_installed(:ingress) if @install_ingress
- show.await_installed(:prometheus) if @install_prometheus
- show.await_installed(:runner) if @install_runner
-
if @install_ingress
populate(:ingress_ip)
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index 5a24bb32475..8d9de0ea718 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -1,17 +1,12 @@
# frozen_string_literal: true
require 'securerandom'
-require 'active_support/core_ext/object/blank'
module QA
module Resource
class MergeRequest < Base
attr_accessor :approval_rules,
- :id,
- :title,
- :description,
:source_branch,
- :target_branch,
:target_new_branch,
:assignee,
:milestone,
@@ -22,9 +17,12 @@ module QA
:wait_for_merge,
:template
- attribute :merge_when_pipeline_succeeds
- attribute :merge_status
- attribute :state
+ attributes :iid,
+ :title,
+ :description,
+ :merge_when_pipeline_succeeds,
+ :merge_status,
+ :state
attribute :project do
Project.fabricate! do |resource|
@@ -32,11 +30,15 @@ module QA
end
end
+ attribute :target_branch do
+ project.default_branch
+ end
+
attribute :target do
Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.branch_name = target_branch
- resource.new_branch = @target_new_branch
+ resource.new_branch = target_new_branch
resource.remote_branch = target_branch
end
end
@@ -62,13 +64,14 @@ module QA
@labels = []
@file_name = "added_file-#{SecureRandom.hex(8)}.txt"
@file_content = "File Added"
- @target_branch = project.default_branch
@target_new_branch = true
@no_preparation = false
@wait_for_merge = true
end
def fabricate!
+ return fabricate_large_merge_request if Runtime::Scenario.large_setup?
+
populate_target_and_source_if_required
project.visit!
@@ -89,21 +92,21 @@ module QA
end
def fabricate_via_api!
- raise ResourceNotFoundError unless id
+ return fabricate_large_merge_request if Runtime::Scenario.large_setup?
resource_web_url(api_get)
- rescue ResourceNotFoundError
+ rescue ResourceNotFoundError, NoValueError # rescue if iid not populated
populate_target_and_source_if_required
super
end
def api_merge_path
- "/projects/#{project.id}/merge_requests/#{id}/merge"
+ "/projects/#{project.id}/merge_requests/#{iid}/merge"
end
def api_get_path
- "/projects/#{project.id}/merge_requests/#{id}"
+ "/projects/#{project.id}/merge_requests/#{iid}"
end
def api_post_path
@@ -112,18 +115,22 @@ module QA
def api_post_body
{
- description: @description,
- source_branch: @source_branch,
- target_branch: @target_branch,
- title: @title
+ description: description,
+ source_branch: source_branch,
+ target_branch: target_branch,
+ title: title
}
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 '#{id}' can be merged")
+ QA::Runtime::Logger.debug("Waiting until merge request with id '#{iid}' can be merged")
- reload!.api_resource[:merge_status] == 'can_be_merged'
+ reload!.merge_status == 'can_be_merged'
end
Support::Retrier.retry_on_exception do
@@ -141,12 +148,21 @@ module QA
end
end
- def reload!
- # Refabricate so that we can return a new object with updated attributes
- self.class.fabricate_via_api! do |resource|
- resource.project = project
- resource.id = api_resource[:iid]
- end
+ def fabricate_large_merge_request
+ @project = Resource::ImportProject.fabricate_via_browser_ui!
+ # Setting the name here, since otherwise some tests will look for an existing file in
+ # the proejct without ever knowing what is in it.
+ @file_name = "github_controller_spec.rb"
+ visit("#{project.web_url}/-/merge_requests/1")
+ current_url
+ end
+
+ # Get MR comments
+ #
+ # @return [Array]
+ def comments
+ response = get(Runtime::API::Request.new(api_client, api_comments_path).url)
+ parse_body(response)
end
private
@@ -158,8 +174,6 @@ module QA
end
def populate_target_and_source_if_required
- @target_branch ||= project.default_branch
-
populate(:target, :source) unless @no_preparation
end
end
diff --git a/qa/qa/resource/package.rb b/qa/qa/resource/package.rb
index 1009353a296..0e8c3ee95de 100644
--- a/qa/qa/resource/package.rb
+++ b/qa/qa/resource/package.rb
@@ -15,11 +15,10 @@ module QA
end
attribute :id do
- packages = project.packages
-
- return unless (this_package = packages&.find { |package| package[:name] == "#{project.path_with_namespace}/#{name}" }) # rubocop:disable Cop/AvoidReturnFromBlocks
+ this_package = project.packages
+ &.find { |package| package[:name] == name }
- this_package[:id]
+ this_package.try(:fetch, :id)
end
def fabricate!
diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb
index 59ae8f4de7a..924e4206166 100644
--- a/qa/qa/resource/personal_access_token.rb
+++ b/qa/qa/resource/personal_access_token.rb
@@ -11,21 +11,25 @@ module QA
# This *could* be different than the api_client.user or the api_user provided by the QA::Resource::ApiFabricator module
attr_writer :user
- attribute :token do
- Page::Profile::PersonalAccessTokens.perform(&:created_access_token)
- end
+ attribute :token
# Only Admins can create PAT via the API.
# If Runtime::Env.admin_personal_access_token is provided, fabricate via the API,
# else, fabricate via the browser.
def fabricate_via_api!
- if Runtime::Env.admin_personal_access_token && !@user.nil?
- self.api_client = Runtime::API::Client.as_admin
+ @token = QA::Resource::PersonalAccessTokenCache.get_token_for_username(user.username)
+ return if @token
- super
- else
- fabricate!
- end
+ resource = if Runtime::Env.admin_personal_access_token && !@user.nil?
+ self.api_client = Runtime::API::Client.as_admin
+
+ super
+ else
+ fabricate!
+ end
+
+ QA::Resource::PersonalAccessTokenCache.set_token_for_username(user.username, self.token)
+ resource
end
# When a user is not provided, use default user
@@ -66,6 +70,8 @@ module QA
# Expire in 2 days just in case the token is created just before midnight
token_page.fill_expiry_date(Time.now.utc.to_date + 2)
token_page.click_create_token_button
+
+ self.token = Page::Profile::PersonalAccessTokens.perform(&:created_access_token)
end
end
end
diff --git a/qa/qa/resource/personal_access_token_cache.rb b/qa/qa/resource/personal_access_token_cache.rb
new file mode 100644
index 00000000000..617779173bd
--- /dev/null
+++ b/qa/qa/resource/personal_access_token_cache.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class PersonalAccessTokenCache
+ @personal_access_tokens = {}
+
+ def self.get_token_for_username(username)
+ @personal_access_tokens[username]
+ end
+
+ def self.set_token_for_username(username, token)
+ @personal_access_tokens[username] = token
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index f8cf816d6e4..d111b070863 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -9,21 +9,23 @@ module QA
include Members
include Visibility
- attr_accessor :repository_storage # requires admin access
- attr_writer :initialize_with_readme,
- :auto_devops_enabled,
- :github_personal_access_token,
- :github_repository_path
-
- attribute :id
- attribute :name
- attribute :add_name_uuid
- attribute :description
- attribute :standalone
- attribute :runners_token
- attribute :visibility
- attribute :template_name
- attribute :import
+ attr_accessor :repository_storage, # requires admin access
+ :initialize_with_readme,
+ :auto_devops_enabled,
+ :github_personal_access_token,
+ :github_repository_path
+
+ attributes :id,
+ :name,
+ :add_name_uuid,
+ :description,
+ :standalone,
+ :runners_token,
+ :visibility,
+ :template_name,
+ :import,
+ :import_status,
+ :import_error
attribute :group do
Group.fabricate!
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
index 93cd166a191..214e8f517bb 100644
--- a/qa/qa/resource/project_imported_from_github.rb
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -1,25 +1,82 @@
# frozen_string_literal: true
-require 'securerandom'
+require 'octokit'
module QA
module Resource
class ProjectImportedFromGithub < Resource::Project
+ attribute :github_repo_id do
+ github_client.repository(github_repository_path).id
+ end
+
def fabricate!
self.import = true
Page::Main::Menu.perform(&:go_to_create_project)
+ go_to_import_page
+
+ Page::Project::Import::Github.perform do |import_page|
+ import_page.add_personal_access_token(github_personal_access_token)
+ import_page.import!(github_repository_path, name)
+ import_page.go_to_project(name)
+ end
+ end
+
+ def go_to_import_page
Page::Project::New.perform do |project_page|
project_page.click_import_project
project_page.click_github_link
end
+ end
- Page::Project::Import::Github.perform do |import_page|
- import_page.add_personal_access_token(@github_personal_access_token)
- import_page.import!(@github_repository_path, @name)
+ def fabricate_via_api!
+ super
+ rescue ResourceURLMissingError
+ "#{Runtime::Scenario.gitlab_address}/#{group.full_path}/#{name}"
+ end
+
+ def api_post_path
+ '/import/github'
+ end
+
+ def api_trigger_mirror_pull_path
+ "#{api_get_path}/mirror/pull"
+ end
+
+ def api_post_body
+ {
+ repo_id: github_repo_id,
+ new_name: name,
+ target_namespace: group.full_path,
+ personal_access_token: github_personal_access_token,
+ ci_cd_only: false
+ }
+ end
+
+ def transform_api_resource(api_resource)
+ api_resource
+ end
+
+ def trigger_project_mirror
+ Runtime::Logger.info "Triggering pull mirror request"
+
+ Support::Retrier.retry_until(max_attempts: 6, sleep_interval: 10) do
+ response = post(request_url(api_trigger_mirror_pull_path), nil)
+
+ Runtime::Logger.info "Mirror pull request response: #{response}"
+ response.code == Support::Api::HTTP_STATUS_OK
end
end
+
+ private
+
+ # Github client
+ #
+ # @return [Octokit::Client]
+ def github_client
+ @github_client ||= Octokit::Client.new(access_token: github_personal_access_token)
+ end
end
end
end
diff --git a/qa/qa/resource/project_imported_from_url.rb b/qa/qa/resource/project_imported_from_url.rb
new file mode 100644
index 00000000000..f159a174840
--- /dev/null
+++ b/qa/qa/resource/project_imported_from_url.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+
+module QA
+ module Resource
+ class ProjectImportedFromURL < Resource::Project
+ def fabricate!
+ self.import = true
+ super
+
+ group.visit!
+
+ Page::Group::Show.perform(&:go_to_new_project)
+
+ Page::Project::New.perform do |project_page|
+ project_page.click_import_project
+ project_page.click_repo_by_url_link
+ end
+
+ Page::Project::Import::RepoByURL.perform do |import_page|
+ import_page.import!(@gitlab_repository_path, @name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/repository/wiki_push.rb b/qa/qa/resource/repository/wiki_push.rb
index edf76c7cd78..aa69d831bff 100644
--- a/qa/qa/resource/repository/wiki_push.rb
+++ b/qa/qa/resource/repository/wiki_push.rb
@@ -16,7 +16,6 @@ module QA
@file_name = 'Home.md'
@file_content = 'This line was created using git push'
@commit_message = 'Updating using git push'
- @branch_name = 'master'
@new_branch = false
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 8957dbcbe84..c424d7319fe 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -25,7 +25,7 @@ module QA
end
def self.default
- Resource::User.new.tap do |user|
+ Resource::User.init do |user|
user.username = Runtime::User.ldap_user? ? Runtime::User.ldap_username : Runtime::User.username
user.password = Runtime::User.ldap_user? ? Runtime::User.ldap_password : Runtime::User.password
end
diff --git a/qa/qa/runtime/allure_report.rb b/qa/qa/runtime/allure_report.rb
index 5e9ae3e7bbe..bcfdb09e09f 100644
--- a/qa/qa/runtime/allure_report.rb
+++ b/qa/qa/runtime/allure_report.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'active_support/core_ext/enumerable'
+
module QA
module Runtime
class AllureReport
@@ -29,6 +31,7 @@ module QA
AllureRspec.configure do |config|
config.results_directory = 'tmp/allure-results'
config.clean_results_directory = true
+ config.environment_properties = environment_info if Env.running_in_ci?
# Set custom environment name to separate same specs executed on different environments
if Env.running_in_ci? && Env.ci_job_name.match?(env_matcher)
@@ -66,19 +69,48 @@ module QA
RSpec.configure do |config|
config.formatter = AllureRspecFormatter
- config.before do |example|
+ config.after do |example|
next if example.attempts && example.attempts > 0
testcase = example.metadata[:testcase]
example.tms('Testcase', testcase) if testcase
- issue = example.metadata.dig(:quarantine, :issue)
- example.issue('Issue', issue) if issue
+ quarantine_issue = example.metadata.dig(:quarantine, :issue)
+ example.issue('Quarantine issue', quarantine_issue) if quarantine_issue
+
+ spec_file = example.file_path.split('/').last
+ example.issue(
+ 'Failure issues',
+ "https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{spec_file}"
+ )
example.add_link(name: "Job(#{Env.ci_job_name})", url: Env.ci_job_url) if Env.running_in_ci?
end
end
end
+
+ # Custom environment info hash
+ #
+ # @return [Hash]
+ def environment_info
+ %w[
+ CI_COMMIT_SHA
+ CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
+ CI_MERGE_REQUEST_IID
+ TOP_UPSTREAM_SOURCE_SHA
+ TOP_UPSTREAM_MERGE_REQUEST_IID
+ DEPLOY_VERSION
+ GITLAB_VERSION
+ GITLAB_SHELL_VERSION
+ GITLAB_ELASTICSEARCH_INDEXER_VERSION
+ GITLAB_KAS_VERSION
+ GITLAB_WORKHORSE_VERSION
+ GITLAB_PAGES_VERSION
+ GITALY_SERVER_VERSION
+ QA_IMAGE
+ QA_BROWSER
+ ].index_with { |val| ENV[val] }.compact_blank
+ end
end
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 3fe8552c063..e13061e2648 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -5,6 +5,8 @@ require 'rspec/expectations'
require 'capybara/rspec'
require 'capybara-screenshot/rspec'
require 'selenium-webdriver'
+require 'webdrivers/chromedriver'
+require 'webdrivers/geckodriver'
require 'gitlab_handbook'
@@ -66,61 +68,82 @@ module QA
return if Capybara.drivers.include?(:chrome)
Capybara.register_driver QA::Runtime::Env.browser do |app|
- capabilities = Selenium::WebDriver::Remote::Capabilities.send(QA::Runtime::Env.browser,
- # This enables access to logs with `page.driver.manage.get_log(:browser)`
- loggingPrefs: {
- browser: "ALL",
- client: "ALL",
- driver: "ALL",
- server: "ALL"
- })
-
- if QA::Runtime::Env.accept_insecure_certs?
- capabilities['acceptInsecureCerts'] = true
- end
+ capabilities = Selenium::WebDriver::Remote::Capabilities.send(QA::Runtime::Env.browser)
- # QA::Runtime::Env.browser.capitalize will work for every driver type except PhantomJS.
- # We will have no use to use PhantomJS so this shouldn't be a problem.
- options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize, false)::Options.new
+ case QA::Runtime::Env.browser
+ when :chrome
+ if QA::Runtime::Env.accept_insecure_certs?
+ capabilities['acceptInsecureCerts'] = true
+ end
- if QA::Runtime::Env.browser == :chrome
- options.add_argument("window-size=1480,2200")
+ # set logging preferences
+ # this enables access to logs with `page.driver.manage.get_log(:browser)`
+ capabilities['goog:loggingPrefs'] = {
+ browser: 'ALL',
+ client: 'ALL',
+ driver: 'ALL',
+ server: 'ALL'
+ }
# Chrome won't work properly in a Docker container in sandbox mode
- options.add_argument("no-sandbox")
+ capabilities['goog:chromeOptions'] = {
+ args: %w[no-sandbox]
+ }
- # Run headless by default unless CHROME_HEADLESS is false
- if QA::Runtime::Env.chrome_headless?
- options.add_argument("headless")
+ # Run headless by default unless WEBDRIVER_HEADLESS is false
+ if QA::Runtime::Env.webdriver_headless?
+ capabilities['goog:chromeOptions'][:args] << 'headless'
# Chrome documentation says this flag is needed for now
# https://developers.google.com/web/updates/2017/04/headless-chrome#cli
- options.add_argument("disable-gpu")
+ capabilities['goog:chromeOptions'][:args] << 'disable-gpu'
end
# Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252
- options.add_argument("disable-dev-shm-usage") if QA::Runtime::Env.running_in_ci?
+ capabilities['goog:chromeOptions'][:args] << 'disable-dev-shm-usage' if QA::Runtime::Env.running_in_ci?
# Specify the user-agent to allow challenges to be bypassed
# See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
- options.add_argument("user-agent=#{QA::Runtime::Env.user_agent}") if QA::Runtime::Env.user_agent
+ capabilities['goog:chromeOptions'][:args] << "user-agent=#{QA::Runtime::Env.user_agent}" if QA::Runtime::Env.user_agent
+
+ if QA::Runtime::Env.remote_mobile_device_name
+ capabilities['platformName'] = 'Android'
+ capabilities['appium:deviceName'] = QA::Runtime::Env.remote_mobile_device_name
+ capabilities['appium:platformVersion'] = 'latest'
+ else
+ capabilities['goog:chromeOptions'][:args] << 'window-size=1480,2200'
+ end
+
+ when :safari
+ if QA::Runtime::Env.remote_mobile_device_name
+ capabilities['platformName'] = 'iOS'
+ capabilities['appium:deviceName'] = QA::Runtime::Env.remote_mobile_device_name
+ capabilities['appium:platformVersion'] = 'latest'
+ end
+
+ when :firefox
+ if QA::Runtime::Env.accept_insecure_certs?
+ capabilities['acceptInsecureCerts'] = true
+ end
end
# Use the same profile on QA runs if CHROME_REUSE_PROFILE is true.
# Useful to speed up local QA.
if QA::Runtime::Env.reuse_chrome_profile?
qa_profile_dir = ::File.expand_path('../../tmp/qa-profile', __dir__)
- options.add_argument("user-data-dir=#{qa_profile_dir}")
+ capabilities['goog:chromeOptions'][:args] << "user-data-dir=#{qa_profile_dir}"
end
selenium_options = {
browser: QA::Runtime::Env.browser,
clear_local_storage: true,
- desired_capabilities: capabilities,
- options: options
+ capabilities: capabilities
}
- selenium_options[:url] = QA::Runtime::Env.remote_grid if QA::Runtime::Env.remote_grid
+ if QA::Runtime::Env.remote_grid
+ selenium_options[:url] = QA::Runtime::Env.remote_grid
+ capabilities[:browserVersion] = 'latest'
+ end
Capybara::Selenium::Driver.new(
app,
@@ -158,6 +181,7 @@ module QA
config.browser = Capybara.current_session.driver.browser # reuse Capybara session
config.libraries = [GitlabHandbook]
config.base_url = Runtime::Scenario.attributes[:gitlab_address] # reuse GitLab address
+ config.hide_banner = true
end
end
# rubocop: enable Metrics/AbcSize
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index ccb87147a6e..5cac811d95b 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+require 'active_support/deprecation'
require 'gitlab/qa'
require 'uri'
@@ -64,8 +65,14 @@ module QA
ENV['QA_LOG_PATH'] || $stdout
end
- # set to 'false' to have Chrome run visibly instead of headless
- def chrome_headless?
+ # set to 'false' to have the browser run visibly instead of headless
+ def webdriver_headless?
+ if ENV.key?('CHROME_HEADLESS')
+ ActiveSupport::Deprecation.warn("CHROME_HEADLESS is deprecated. Use WEBDRIVER_HEADLESS instead.")
+ end
+
+ return enabled?(ENV['WEBDRIVER_HEADLESS']) unless ENV['WEBDRIVER_HEADLESS'].nil?
+
enabled?(ENV['CHROME_HEADLESS'])
end
@@ -139,6 +146,10 @@ module QA
ENV['QA_BROWSER'].nil? ? :chrome : ENV['QA_BROWSER'].to_sym
end
+ def remote_mobile_device_name
+ ENV['QA_REMOTE_MOBILE_DEVICE_NAME']
+ end
+
def user_username
ENV['GITLAB_USERNAME']
end
@@ -327,7 +338,7 @@ module QA
# the feature is supported in the environment under test.
# All features are supported by default.
def can_test?(feature)
- raise ArgumentError, %Q(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature
+ raise ArgumentError, %(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature
enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
end
@@ -385,11 +396,17 @@ module QA
ENV.fetch('GITLAB_QA_TRANSIENT_TRIALS', 10).to_i
end
+ def gitlab_tls_certificate
+ ENV['GITLAB_TLS_CERTIFICATE']
+ end
+
private
def remote_grid_credentials
if remote_grid_username
- raise ArgumentError, %Q(Please provide an access key for user "#{remote_grid_username}") unless remote_grid_access_key
+ unless remote_grid_access_key
+ raise ArgumentError, %(Please provide an access key for user "#{remote_grid_username}")
+ end
return "#{remote_grid_username}:#{remote_grid_access_key}@"
end
diff --git a/qa/qa/scenario/test/integration/registry.rb b/qa/qa/scenario/test/integration/registry.rb
new file mode 100644
index 00000000000..28b74c99ab3
--- /dev/null
+++ b/qa/qa/scenario/test/integration/registry.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Registry < Test::Instance::All
+ tags :registry
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/cluster_provider/k3s_cilium.rb b/qa/qa/service/cluster_provider/k3s_cilium.rb
new file mode 100644
index 00000000000..5b529caa20b
--- /dev/null
+++ b/qa/qa/service/cluster_provider/k3s_cilium.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module ClusterProvider
+ class K3sCilium < K3s
+ def setup
+ @k3s = Service::DockerRun::K3s.new.tap do |k3s|
+ k3s.remove!
+ k3s.cni_enabled = true
+ k3s.register!
+
+ shell "kubectl config set-cluster k3s --server https://#{k3s.host_name}:6443 --insecure-skip-tls-verify"
+ shell 'kubectl config set-credentials default --username=node --password=some-secret'
+ shell 'kubectl config set-context k3s --cluster=k3s --user=default'
+ shell 'kubectl config use-context k3s'
+
+ wait_for_server(k3s.host_name) do
+ shell 'kubectl version'
+ # install local storage
+ shell 'kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml'
+
+ # patch local storage
+ shell %(kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}')
+ shell 'kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.8/install/kubernetes/quick-install.yaml'
+
+ wait_for_namespaces do
+ wait_for_cilium
+ wait_for_coredns do
+ shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
+ wait_for_ingress
+ end
+ end
+ end
+ end
+ end
+
+ private
+
+ def wait_for_cilium
+ QA::Runtime::Logger.info 'Waiting for Cilium pod to be initialized'
+
+ 60.times do
+ if service_available?('kubectl get pods --all-namespaces -l k8s-app=cilium --no-headers=true | grep -o "cilium-.*1/1"')
+ return yield if block_given?
+
+ return true
+ end
+
+ sleep 1
+ QA::Runtime::Logger.info '.'
+ end
+
+ raise 'Cilium pod has not initialized correctly'
+ end
+
+ def wait_for_coredns
+ QA::Runtime::Logger.info 'Waiting for CoreDNS pod to be initialized'
+
+ 60.times do
+ if service_available?('kubectl get pods --all-namespaces --no-headers=true | grep -o "coredns.*1/1"')
+ return yield if block_given?
+
+ return true
+ end
+
+ sleep 1
+ QA::Runtime::Logger.info '.'
+ end
+
+ raise 'CoreDNS pod has not been initialized correctly'
+ end
+
+ def wait_for_ingress
+ QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
+
+ 60.times do
+ if service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
+ return yield if block_given?
+
+ return true
+ end
+
+ sleep 1
+ QA::Runtime::Logger.info '.'
+ end
+
+ raise 'Ingress pod has not been initialized correctly'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
index a5b129eb1f9..63fbf758231 100644
--- a/qa/qa/service/docker_run/gitlab_runner.rb
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -38,11 +38,10 @@ module QA
def register!
shell <<~CMD.tr("\n", ' ')
- docker run -d --rm --entrypoint=/bin/sh
- --network #{runner_network} --name #{@name}
+ docker run -d --rm --network #{runner_network} --name #{@name}
#{'-v /var/run/docker.sock:/var/run/docker.sock' if @executor == :docker}
--privileged
- #{@image} -c "#{register_command}"
+ #{@image} #{add_gitlab_tls_cert if @address.include? "https"} && docker exec --detach #{@name} sh -c "#{register_command}"
CMD
# Prove airgappedness
@@ -82,6 +81,7 @@ module QA
args << '--docker-tlsverify=false'
args << '--docker-privileged=true'
args << "--docker-network-mode=#{network}"
+ args << "--docker-volumes=/certs/client"
end
<<~CMD.strip
@@ -102,6 +102,16 @@ module QA
wget --retry-connrefused --waitretry=1 --read-timeout=15 --timeout=10 -t 2 http://registry.gitlab.com > /dev/null 2>&1 && (echo "Airgapped network faulty. Connectivity wget check failed." && exit 1) || (echo "Airgapped network confirmed. Connectivity wget check passed." && exit 0)
CMD
end
+
+ def add_gitlab_tls_cert
+ gitlab_tls_certificate = Tempfile.new('gitlab-cert')
+ gitlab_tls_certificate.write(Runtime::Env.gitlab_tls_certificate)
+ gitlab_tls_certificate.close
+
+ <<~CMD
+ && docker cp #{gitlab_tls_certificate.path} #{@name}:/etc/gitlab-runner/certs/gitlab.test.crt
+ CMD
+ end
end
end
end
diff --git a/qa/qa/service/docker_run/k3s.rb b/qa/qa/service/docker_run/k3s.rb
index 07211b220f1..a09b62cb613 100644
--- a/qa/qa/service/docker_run/k3s.rb
+++ b/qa/qa/service/docker_run/k3s.rb
@@ -4,15 +4,20 @@ module QA
module Service
module DockerRun
class K3s < Base
+ attr_accessor :cni_enabled
+
def initialize
- @image = 'registry.gitlab.com/gitlab-org/cluster-integration/test-utils/k3s-gitlab-ci/releases/v0.6.1'
+ @image = 'registry.gitlab.com/gitlab-org/cluster-integration/test-utils/k3s-gitlab-ci/releases/v0.9.1'
@name = 'k3s'
+ @cni_enabled = false
super
end
def register!
pull
start_k3s
+ # Mount the berkeley packet filter if container network interface is enabled
+ mount_bpf if @cni_enabled
end
def host_name
@@ -36,12 +41,20 @@ module QA
#{@image} server
--cluster-secret some-secret
--no-deploy traefik
+ #{@cni_enabled ? '--no-flannel' : ''}
CMD
command.gsub!("--network #{network} --hostname #{host_name}", '') unless QA::Runtime::Env.running_in_ci?
shell command
end
+
+ private
+
+ def mount_bpf
+ shell "docker exec --privileged k3s mount bpffs -t bpf /sys/fs/bpf"
+ shell "docker exec --privileged k3s mount --make-shared bpffs -t bpf /sys/fs/bpf"
+ end
end
end
end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index ddf97046fb0..adef1b46af2 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -51,6 +51,30 @@ module QA
shell('kubectl apply -f -', stdin_data: manifest)
end
+ def add_sample_policy(project, policy_name: 'sample-policy')
+ namespace = "#{project.name}-#{project.id}-production"
+ network_policy = <<~YAML
+ apiVersion: "cilium.io/v2"
+ kind: CiliumNetworkPolicy
+ metadata:
+ name: #{policy_name}
+ namespace: #{namespace}
+ spec:
+ endpointSelector:
+ matchLabels:
+ role: backend
+ ingress:
+ - fromEndpoints:
+ - matchLabels:
+ role: frontend
+ YAML
+ shell('kubectl apply -f -', stdin_data: network_policy)
+ end
+
+ def fetch_external_ip_for_ingress
+ `kubectl get svc --all-namespaces --no-headers=true -l app.kubernetes.io/name=ingress-nginx -o custom-columns=:'status.loadBalancer.ingress[0].ip' | grep -v 'none'`
+ end
+
private
def fetch_api_url
diff --git a/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb
new file mode 100644
index 00000000000..1b873d35d75
--- /dev/null
+++ b/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage', :github, :requires_admin do
+ describe 'Project import' do
+ let!(:api_client) { Runtime::API::Client.as_admin }
+ let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |resource|
+ resource.api_client = api_client
+ resource.hard_delete_on_api_removal = true
+ end
+ end
+
+ let(:imported_project) do
+ Resource::ProjectImportedFromGithub.fabricate_via_api! do |project|
+ project.name = 'imported-project'
+ project.group = group
+ project.github_personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = 'gitlab-qa-github/test-project'
+ project.api_client = api_client
+ end
+ end
+
+ before do
+ group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ end
+
+ after do
+ user.remove_via_api!
+ end
+
+ it 'imports Github repo via api', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1858' do
+ imported_project # import the project
+
+ expect { imported_project.reload!.import_status }.to eventually_eq('finished').within(duration: 90)
+
+ aggregate_failures do
+ verify_repository_import
+ verify_commits_import
+ verify_labels_import
+ verify_issues_import
+ verify_milestones_import
+ verify_wikis_import
+ verify_merge_requests_import
+ end
+ end
+
+ def verify_repository_import
+ expect(imported_project.api_response).to include(
+ description: 'A new repo for test',
+ import_error: nil
+ )
+ end
+
+ def verify_commits_import
+ expect(imported_project.commits.length).to eq(20)
+ end
+
+ def verify_labels_import
+ labels = imported_project.labels.map { |label| label.slice(:name, :color) }
+
+ expect(labels).to include(
+ { name: 'bug', color: '#d73a4a' },
+ { name: 'custom new label', color: '#fc8f91' },
+ { name: 'documentation', color: '#0075ca' },
+ { name: 'duplicate', color: '#cfd3d7' },
+ { name: 'enhancement', color: '#a2eeef' },
+ { name: 'good first issue', color: '#7057ff' },
+ { name: 'help wanted', color: '#008672' },
+ { name: 'invalid', color: '#e4e669' },
+ { name: 'question', color: '#d876e3' },
+ { name: 'wontfix', color: '#ffffff' }
+ )
+ end
+
+ def verify_issues_import
+ issues = imported_project.issues
+
+ expect(issues.length).to eq(1)
+ expect(issues.first).to include(
+ title: 'This is a sample issue',
+ description: "*Created by: gitlab-qa-github*\n\nThis is a sample first comment",
+ labels: ['custom new label', 'good first issue', 'help wanted'],
+ user_notes_count: 1
+ )
+ end
+
+ def verify_milestones_import
+ milestones = imported_project.milestones
+
+ expect(milestones.length).to eq(1)
+ expect(milestones.first).to include(title: 'v1.0', description: nil, state: 'active')
+ end
+
+ def verify_wikis_import
+ wikis = imported_project.wikis
+
+ expect(wikis.length).to eq(1)
+ expect(wikis.first).to include(title: 'Home', format: 'markdown')
+ end
+
+ def verify_merge_requests_import
+ merge_requests = imported_project.merge_requests
+ merge_request = Resource::MergeRequest.init do |mr|
+ mr.project = imported_project
+ 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
+
+ expect(merge_requests.length).to eq(1)
+ expect(merge_request.api_resource).to include(
+ title: 'Improve readme',
+ state: 'opened',
+ target_branch: 'main',
+ source_branch: 'improve-readme',
+ labels: %w[bug documentation],
+ description: <<~DSC.strip
+ *Created by: gitlab-qa-github*\n\nThis improves the README file a bit.\r\n\r\nTODO:\r\n\r\n \r\n\r\n- [ ] Do foo\r\n- [ ] Make bar\r\n - [ ] Think about baz
+ DSC
+ )
+ expect(mr_comments).to eq(
+ [
+ "*Created by: gitlab-qa-github*\n\n[PR comment by @sliaquat] Nice work! ",
+ "*Created by: gitlab-qa-github*\n\n[Single diff comment] Nice addition",
+ "*Created by: gitlab-qa-github*\n\n[Single diff comment] Good riddance"
+ ]
+ )
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
index 08b71258cc6..6c70c09c7ab 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
+++ b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
@@ -5,8 +5,7 @@ require 'parallel'
module QA
RSpec.describe 'Create' do
context 'Gitaly' do
- # Issue to track removal of feature flag: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/602
- describe 'Distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/322814', type: :investigating } do
+ describe 'Distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin do
let(:number_of_reads_per_loop) { 9 }
let(:praefect_manager) { Service::PraefectManager.new }
let(:project) do
@@ -17,14 +16,9 @@ module QA
end
before do
- Runtime::Feature.enable(:gitaly_distributed_reads)
praefect_manager.wait_for_replication(project.id)
end
- after do
- Runtime::Feature.disable(:gitaly_distributed_reads)
- end
-
it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/979' do
pre_read_data = praefect_manager.query_read_distribution
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
index 396d3f52798..7e924475437 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
+++ b/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
@@ -4,7 +4,7 @@ require 'parallel'
module QA
RSpec.describe 'Create' do
- context 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/331989', type: :investigating } do
+ context 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
let(:praefect_manager) { Service::PraefectManager.new }
let(:project) do
Resource::Project.fabricate! do |project|
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb
index dde4708874d..157a9e92817 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb
@@ -64,7 +64,7 @@ module QA
merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = project
- mr.id = merge_request[:iid]
+ mr.iid = merge_request[:iid]
end
expect(merge_request.state).to eq('opened')
@@ -109,7 +109,7 @@ module QA
merge_request = Support::Waiter.wait_until(sleep_interval: 5) do
mr = Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = project
- mr.id = merge_request[:iid]
+ mr.iid = merge_request[:iid]
end
next unless mr.state == 'merged'
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb
index bf2ecfdb513..eb93f4cd5cb 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb
@@ -34,7 +34,7 @@ module QA
merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = project
- mr.id = merge_request[:iid]
+ mr.iid = merge_request[:iid]
end.merge_via_api!
expect(merge_request[:state]).to eq('merged')
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
index e02d32bc4c7..799efc243d4 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
@@ -55,7 +55,7 @@ module QA
merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = project
- mr.id = merge_request[:iid]
+ mr.iid = merge_request[:iid]
end.merge_via_api!
expect(merge_request[:state]).to eq('merged')
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb
index f49a8a229dc..62e6290183f 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb
@@ -39,7 +39,7 @@ module QA
merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = project
- mr.id = merge_request[:iid]
+ mr.iid = merge_request[:iid]
end.merge_via_api!
expect(merge_request[:state]).to eq('merged')
diff --git a/qa/qa/specs/features/api/5_package/container_registry_spec.rb b/qa/qa/specs/features/api/5_package/container_registry_spec.rb
index 57b059ffc02..5003d49fe6c 100644
--- a/qa/qa/specs/features/api/5_package/container_registry_spec.rb
+++ b/qa/qa/specs/features/api/5_package/container_registry_spec.rb
@@ -17,8 +17,8 @@ module QA
end
let(:registry) do
- Resource::RegistryRepository.new.tap do |repository|
- repository.name = "#{project.path_with_namespace}"
+ Resource::RegistryRepository.init do |repository|
+ repository.name = project.path_with_namespace
repository.project = project
repository.tag_name = 'master'
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb
index d4c4ec5611a..6c2ff005f49 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/bulk_import_group_spec.rb
@@ -3,31 +3,33 @@
module QA
RSpec.describe 'Manage', :requires_admin do
describe 'Bulk group import' do
- let!(:admin_api_client) { Runtime::API::Client.as_admin }
- let!(:user) do
+ let!(:staging?) { Runtime::Scenario.gitlab_address.include?('staging.gitlab.com') }
+
+ let(:admin_api_client) { Runtime::API::Client.as_admin }
+ let(:user) do
Resource::User.fabricate_via_api! do |usr|
usr.api_client = admin_api_client
usr.hard_delete_on_api_removal = true
end
end
- let!(:api_client) { Runtime::API::Client.new(user: user) }
- let!(:personal_access_token) { api_client.personal_access_token }
+ let(:api_client) { Runtime::API::Client.new(user: user) }
+ let(:personal_access_token) { api_client.personal_access_token }
- let!(:sandbox) do
+ let(:sandbox) do
Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = admin_api_client
end
end
- let!(:source_group) do
+ let(:source_group) do
Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = api_client
group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
end
end
- let!(:subgroup) do
+ let(:subgroup) do
Resource::Group.fabricate_via_api! do |group|
group.api_client = api_client
group.sandbox = source_group
@@ -36,7 +38,7 @@ module QA
end
let(:imported_group) do
- Resource::Group.new.tap do |group|
+ Resource::Group.init do |group|
group.api_client = api_client
group.sandbox = sandbox
group.path = source_group.path
@@ -44,25 +46,23 @@ module QA
end
let(:imported_subgroup) do
- Resource::Group.new.tap do |group|
+ Resource::Group.init do |group|
group.api_client = api_client
group.sandbox = imported_group
group.path = subgroup.path
end
end
- def staging?
- Runtime::Scenario.gitlab_address.include?('staging.gitlab.com')
- end
-
- before(:all) do
+ before do
Runtime::Feature.enable(:bulk_import) unless staging?
Runtime::Feature.enable(:top_level_group_creation_enabled) if staging?
- end
- before do
sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ # create groups explicitly before connecting gitlab instance
+ source_group
+ subgroup
+
Flow::Login.sign_in(as: user)
Page::Main::Menu.perform(&:go_to_create_group)
Page::Group::New.perform do |group|
@@ -73,14 +73,10 @@ module QA
# Non blocking issues:
# https://gitlab.com/gitlab-org/gitlab/-/issues/331252
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/333678 <- can cause 500 when creating user and group back to back
it(
'imports group with subgroups and labels',
- testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1785',
- quarantine: {
- only: { job: 'relative_url' },
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/330344',
- type: :bug
- }
+ testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1785'
) do
Resource::GroupLabel.fabricate_via_api! do |label|
label.api_client = api_client
@@ -96,9 +92,9 @@ module QA
Page::Group::BulkImport.perform do |import_page|
import_page.import_group(source_group.path, sandbox.path)
- aggregate_failures do
- expect(import_page).to have_imported_group(source_group.path, wait: 180)
+ expect(import_page).to have_imported_group(source_group.path, wait: 180)
+ aggregate_failures do
expect { imported_group.reload! }.to eventually_eq(source_group).within(duration: 10)
expect { imported_group.labels }.to eventually_include(*source_group.labels).within(duration: 10)
@@ -111,9 +107,7 @@ module QA
after do
user.remove_via_api!
- end
-
- after(:all) do
+ ensure
Runtime::Feature.disable(:bulk_import) unless staging?
Runtime::Feature.disable(:top_level_group_creation_enabled) if staging?
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 23a21d70cc1..696bbc2a7b7 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -143,7 +143,7 @@ module QA
Page::Admin::Overview::Users::Show.perform do |show|
user.id = show.user_id.to_i
- show.approve_user
+ show.approve_user(user)
end
expect(page).to have_text('Successfully approved')
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 e722710ee00..4fffc786c14 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
@@ -4,7 +4,7 @@ module QA
RSpec.describe 'Manage', :smoke do
describe 'Project creation' do
it 'user creates a new project',
- testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1234' do
+ testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1857' do
Flow::Login.sign_in
created_project = Resource::Project.fabricate_via_browser_ui! do |project|
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 95092fe6ba6..6e0ed4adb63 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
@@ -6,7 +6,7 @@ module QA
RSpec.describe 'Manage', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/212145', type: :stale } do
describe 'Check for broken images', :requires_admin do
before(:context) do
- admin = QA::Resource::User.new.tap do |user|
+ admin = QA::Resource::User.init do |user|
user.username = QA::Runtime::User.admin_username
user.password = QA::Runtime::User.admin_password
end
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 55ad8baf872..4f85fa257a2 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
@@ -18,7 +18,6 @@ module QA
project.group = group
project.github_personal_access_token = Runtime::Env.github_access_token
project.github_repository_path = 'gitlab-qa-github/test-project'
- project.api_client = api_client
end
end
@@ -33,91 +32,13 @@ module QA
it 'imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1762' do
Flow::Login.sign_in(as: user)
- imported_project.reload! # import the project and reload all fields
+ imported_project # import the project
- aggregate_failures do
- verify_repository_import
- verify_commits_import
- verify_labels_import
- verify_issues_import
- verify_milestones_import
- verify_wikis_import
- verify_merge_requests_import
+ Page::Project::Show.perform do |project|
+ expect(project).to have_content(imported_project.name)
+ expect(project).to have_content('This test project is used for automated GitHub import by GitLab QA.')
end
end
-
- def verify_repository_import
- expect(imported_project.api_response).to include(
- description: 'A new repo for test',
- import_status: 'finished',
- import_error: nil
- )
- end
-
- def verify_commits_import
- expect(imported_project.commits.length).to eq(20)
- end
-
- def verify_labels_import
- labels = imported_project.labels.map { |label| label.slice(:name, :color) }
-
- expect(labels).to eq(
- [
- { name: 'bug', color: '#d73a4a' },
- { name: 'custom new label', color: '#fc8f91' },
- { name: 'documentation', color: '#0075ca' },
- { name: 'duplicate', color: '#cfd3d7' },
- { name: 'enhancement', color: '#a2eeef' },
- { name: 'good first issue', color: '#7057ff' },
- { name: 'help wanted', color: '#008672' },
- { name: 'invalid', color: '#e4e669' },
- { name: 'question', color: '#d876e3' },
- { name: 'wontfix', color: '#ffffff' }
- ]
- )
- end
-
- def verify_issues_import
- issues = imported_project.issues
-
- expect(issues.length).to eq(1)
- expect(issues.first).to include(
- title: 'This is a sample issue',
- description: "*Created by: gitlab-qa-github*\n\nThis is a sample first comment",
- labels: ['custom new label', 'good first issue', 'help wanted'],
- user_notes_count: 1
- )
- end
-
- def verify_milestones_import
- milestones = imported_project.milestones
-
- expect(milestones.length).to eq(1)
- expect(milestones.first).to include(title: 'v1.0', description: nil, state: 'active')
- end
-
- def verify_wikis_import
- wikis = imported_project.wikis
-
- expect(wikis.length).to eq(1)
- expect(wikis.first).to include(title: 'Home', format: 'markdown')
- end
-
- def verify_merge_requests_import
- merge_requests = imported_project.merge_requests
-
- expect(merge_requests.length).to eq(1)
- expect(merge_requests.first).to include(
- title: 'Improve readme',
- state: 'opened',
- target_branch: 'main',
- source_branch: 'improve-readme',
- labels: %w[bug documentation],
- description: <<~DSC.strip
- *Created by: gitlab-qa-github*\n\nThis improves the README file a bit.\r\n\r\nTODO:\r\n\r\n \r\n\r\n- [ ] Do foo\r\n- [ ] Make bar\r\n - [ ] Think about baz
- DSC
- )
- end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
index d352996f419..d561e5d113c 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Email Notification' do
include Support::Api
- let(:user) do
+ let!(:user) 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/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 8c0b3da6004..2243437fc71 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -39,20 +39,23 @@ module QA
end
context 'when using attachments in comments', :object_storage do
- let(:gif_file_name) { 'banana_sample.gif' }
+ let(:png_file_name) { 'testfile.png' }
let(:file_to_attach) do
- File.absolute_path(File.join('qa', 'fixtures', 'designs', gif_file_name))
+ File.absolute_path(File.join('qa', 'fixtures', 'designs', png_file_name))
end
before do
Resource::Issue.fabricate_via_api!.visit!
end
- it 'comments on an issue with an attachment', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1742' do
+ # The following example is excluded from running in `review-qa-smoke` job
+ # as it proved to be flaky when running against Review App
+ # See https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/11568#note_621999351
+ it 'comments on an issue with an attachment', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1742', except: { job: 'review-qa-smoke' } do
Page::Project::Issue::Show.perform do |show|
- show.comment('See attached banana for scale', attachment: file_to_attach)
+ show.comment('See attached image for scale', attachment: file_to_attach)
- expect(show.noteable_note_item.find("img[src$='#{gif_file_name}']")).to be_visible
+ expect(show.noteable_note_item.find("img[src$='#{png_file_name}']")).to be_visible
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
index dd4b3276687..a2b011bc61c 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332588', type: :investigating } do
describe 'Merge request creation from fork' do
# TODO: Please add this back to :smoke suite as soon as https://gitlab.com/gitlab-org/gitlab/-/issues/332588 is addressed
it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1701' do
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb
index 3574cdbe4ac..c05a3610b99 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb
@@ -19,7 +19,7 @@ module QA
Flow::Login.sign_in
end
- it 'can be reverted', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1745' do
+ it 'can be reverted', :can_use_large_setup, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1745' do
revertable_merge_request.visit!
Page::MergeRequest::Show.perform do |merge_request|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
index 67b48d254ac..41fc20cfa5c 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
@@ -8,7 +8,7 @@ module QA
access_token = Resource::PersonalAccessToken.fabricate!.token
- user = Resource::User.new.tap do |user|
+ user = Resource::User.init do |user|
user.username = Runtime::User.username
user.password = access_token
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb
new file mode 100644
index 00000000000..bb9b5feed2e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify' do
+ describe 'Pipeline editor', :requires_admin do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'pipeline-editor-project'
+ end
+ end
+
+ let!(:commit) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: default_file_content
+ }
+ ]
+ )
+ end
+ end
+
+ let!(:production_push) do
+ Resource::Repository::Push.fabricate! do |push|
+ push.repository_http_uri = project.repository_http_location.uri
+ push.branch_name = 'production'
+ push.file_name = '.gitlab-ci.yml'
+ push.file_content = production_file_content
+ end
+ end
+
+ let(:default_file_content) do
+ <<~YAML
+ stages:
+ - test
+
+ initialize:
+ stage: test
+ script:
+ - echo "initialized in #{project.default_branch}"
+ YAML
+ end
+
+ let(:production_file_content) do
+ <<~YAML
+ stages:
+ - test
+
+ initialize:
+ stage: test
+ script:
+ - echo "initialized in production"
+ YAML
+ end
+
+ before do
+ Runtime::Feature.enable(:pipeline_editor_branch_switcher)
+ Flow::Login.sign_in
+ project.visit!
+ Page::Project::Menu.perform(&:go_to_pipeline_editor)
+ end
+
+ after do
+ Runtime::Feature.disable(:pipeline_editor_branch_switcher)
+ project.remove_via_api!
+ Page::Main::Menu.perform(&:sign_out)
+ end
+
+ it 'can switch branches and target branch field updates accordingly', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1856' do
+ Page::Project::PipelineEditor::Show.perform do |show|
+ expect(show).to have_branch_selector_button
+
+ show.click_branch_selector_button
+ show.select_branch_from_dropdown(production_push.branch_name)
+
+ expect(show.target_branch_name).to eq(production_push.branch_name)
+
+ show.click_branch_selector_button
+ show.select_branch_from_dropdown(project.default_branch)
+
+ expect(show.target_branch_name).to eq(project.default_branch)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
index d6d8729114d..07484feb686 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
@@ -40,7 +40,7 @@ module QA
Page::MergeRequest::Show.perform do |mr_widget|
Support::Retrier.retry_until(max_attempts: 5, sleep_interval: 5) do
- mr_widget.has_pipeline_status?(/Pipeline #\d+ passed/)
+ mr_widget.has_pipeline_status?('passed')
end
expect(mr_widget).to have_content('Test coverage 66.67%')
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
index 2489545782a..61c71b062ae 100644
--- a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
@@ -3,7 +3,7 @@
require 'securerandom'
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'Composer Repository' do
include Runtime::Fixtures
@@ -14,7 +14,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = "my_package-#{SecureRandom.hex(4)}"
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
index a1e2eb1046c..668a1524b1c 100644
--- a/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/335981',
+ type: :bug
+ } do
describe 'Conan Repository' do
include Runtime::Fixtures
@@ -12,7 +16,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = 'conantest'
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/container_registry_omnibus_spec.rb b/qa/qa/specs/features/browser_ui/5_package/container_registry_omnibus_spec.rb
new file mode 100644
index 00000000000..4b7669810ec
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/container_registry_omnibus_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :registry, :orchestrated do
+ describe 'Self-managed Container Registry' do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-registry'
+ project.template_name = 'express'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it "pushes image and deletes tag", testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1743' do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([{
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ build:
+ image: docker:19.03.12
+ stage: build
+ services:
+ - name: docker:19.03.12-dind
+ command:
+ - /bin/sh
+ - -c
+ - |
+ apk add --no-cache openssl
+ true | openssl s_client -showcerts -connect gitlab.test:5050 > /usr/local/share/ca-certificates/gitlab.test.crt
+ update-ca-certificates
+ dockerd-entrypoint.sh || exit
+ variables:
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+ script:
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD gitlab.test:5050
+ - docker build -t $IMAGE_TAG .
+ - docker push $IMAGE_TAG
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ }])
+ end
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('build')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:go_to_container_registry)
+
+ Page::Project::Registry::Show.perform do |registry|
+ expect(registry).to have_registry_repository(project.path_with_namespace)
+
+ registry.click_on_image(project.path_with_namespace)
+ expect(registry).to have_tag('master')
+
+ registry.click_delete
+ expect(registry).not_to have_tag('master')
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
index bbf8ea8c05e..ef5965b29e5 100644
--- a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'Generic Repository' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -10,7 +10,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = "my_package"
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
index 4d4f981f021..32a0670e342 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
@@ -1,41 +1,47 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'Maven Repository with Gradle' do
+ using RSpec::Parameterized::TableSyntax
include Runtime::Fixtures
let(:group_id) { 'com.gitlab.qa' }
let(:artifact_id) { 'maven_gradle' }
let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') }
- let(:auth_token) do
- unless Page::Main::Menu.perform(&:signed_in?)
- Flow::Login.sign_in
- end
+ let(:package_version) { '1.3.7' }
- Resource::PersonalAccessToken.fabricate!.token
- end
+ let(:personal_access_token) { Runtime::Env.personal_access_token }
- let(:project) do
+ let(:package_project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'maven-with-gradle-project'
project.initialize_with_readme = true
+ project.visibility = :private
+ end
+ end
+
+ let(:client_project) do
+ Resource::Project.fabricate_via_api! do |client_project|
+ client_project.name = 'gradle_client'
+ client_project.initialize_with_readme = true
+ client_project.group = package_project.group
end
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = package_name
- package.project = project
+ package.project = package_project
end
end
- let!(:runner) do
+ let(:runner) do
Resource::Runner.fabricate! do |runner|
runner.name = "qa-runner-#{Time.now.to_i}"
- runner.tags = ["runner-for-#{project.name}"]
+ runner.tags = ["runner-for-#{package_project.group.name}"]
runner.executor = :docker
- runner.project = project
+ runner.token = package_project.group.runners_token
end
end
@@ -44,87 +50,200 @@ module QA
"#{uri.scheme}://#{uri.host}:#{uri.port}"
end
+ let(:project_deploy_token) do
+ Resource::DeployToken.fabricate_via_browser_ui! do |deploy_token|
+ deploy_token.name = 'maven-with-gradle-deploy-token'
+ deploy_token.project = package_project
+ end
+ end
+
+ let(:package_gitlab_ci_file) do
+ {
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ deploy:
+ image: gradle:6.5-jdk11
+ script:
+ - 'gradle publish'
+ only:
+ - "#{package_project.default_branch}"
+ tags:
+ - "runner-for-#{package_project.group.name}"
+ YAML
+ }
+ end
+
+ let(:package_build_gradle_file) do
+ {
+ file_path: 'build.gradle',
+ content:
+ <<~EOF
+ plugins {
+ id 'java'
+ id 'maven-publish'
+ }
+
+ publishing {
+ publications {
+ library(MavenPublication) {
+ groupId '#{group_id}'
+ artifactId '#{artifact_id}'
+ version '#{package_version}'
+ from components.java
+ }
+ }
+ repositories {
+ maven {
+ url "#{gitlab_address_with_port}/api/v4/projects/#{package_project.id}/packages/maven"
+ credentials(HttpHeaderCredentials) {
+ name = "Private-Token"
+ value = "#{personal_access_token}"
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+ }
+ }
+ EOF
+ }
+ end
+
+ let(:client_gitlab_ci_file) do
+ {
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ build:
+ image: gradle:6.5-jdk11
+ script:
+ - 'gradle build'
+ only:
+ - "#{client_project.default_branch}"
+ tags:
+ - "runner-for-#{client_project.group.name}"
+ YAML
+ }
+ end
+
+ before do
+ Flow::Login.sign_in_unless_signed_in
+ runner
+ end
+
after do
runner.remove_via_api!
package.remove_via_api!
+ package_project.remove_via_api!
+ client_project.remove_via_api!
end
- it 'publishes a maven package via gradle', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1074' do
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files([{
- file_path: '.gitlab-ci.yml',
- content:
- <<~YAML
- deploy:
- image: gradle:6.5-jdk11
- script:
- - 'gradle publish'
- only:
- - "#{project.default_branch}"
- tags:
- - "runner-for-#{project.name}"
- YAML
- },
- {
- file_path: 'build.gradle',
- content:
- <<~EOF
- plugins {
- id 'java'
- id 'maven-publish'
- }
-
- publishing {
- publications {
- library(MavenPublication) {
- groupId '#{group_id}'
- artifactId '#{artifact_id}'
- from components.java
- }
- }
- repositories {
- maven {
- url "#{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/maven"
- credentials(HttpHeaderCredentials) {
- name = "Private-Token"
- value = "#{auth_token}"
- }
- authentication {
- header(HttpHeaderAuthentication)
- }
- }
- }
- }
- EOF
- }])
- end
-
- project.visit!
- Flow::Pipeline.visit_latest_pipeline
+ where(:authentication_token_type, :maven_header_name) do
+ :personal_access_token | 'Private-Token'
+ :ci_job_token | 'Job-Token'
+ :project_deploy_token | 'Deploy-Token'
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('deploy')
+ with_them do
+ let(:token) do
+ case authentication_token_type
+ when :personal_access_token
+ "\"#{personal_access_token}\""
+ when :ci_job_token
+ 'System.getenv("CI_JOB_TOKEN")'
+ when :project_deploy_token
+ "\"#{project_deploy_token.password}\""
+ end
end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
+ let(:client_build_gradle_file) do
+ {
+ file_path: 'build.gradle',
+ content:
+ <<~EOF
+ plugins {
+ id 'java'
+ id 'application'
+ }
+
+ repositories {
+ jcenter()
+ maven {
+ url "#{gitlab_address_with_port}/api/v4/projects/#{package_project.id}/packages/maven"
+ name "GitLab"
+ credentials(HttpHeaderCredentials) {
+ name = '#{maven_header_name}'
+ value = #{token}
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+ }
+
+ dependencies {
+ implementation group: '#{group_id}', name: '#{artifact_id}', version: '#{package_version}'
+ testImplementation 'junit:junit:4.12'
+ }
+
+ application {
+ mainClassName = 'gradle_maven_app.App'
+ }
+ EOF
+ }
end
- Page::Project::Menu.perform(&:click_packages_link)
+ it "pushes and pulls a maven package via gradle using #{params[:authentication_token_type]}", testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1074' do
+ # pushing
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = package_project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([package_gitlab_ci_file, package_build_gradle_file])
+ end
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_package(package_name)
+ package_project.visit!
- index.click_package(package_name)
- end
+ 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)
+ end
+
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |show|
+ expect(show).to have_package_info(package_name, package_version)
+ end
+
+ # pulling
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = client_project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([client_gitlab_ci_file, client_build_gradle_file])
+ end
+
+ client_project.visit!
+
+ Flow::Pipeline.visit_latest_pipeline
- Page::Project::Packages::Show.perform(&:click_delete)
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('build')
+ end
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_content("Package deleted successfully")
- expect(index).not_to have_package(package_name)
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index 9c00f1f6d17..fb92616ffc5 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -3,7 +3,7 @@
require 'securerandom'
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :reliable do
+ RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
describe 'Maven Repository' do
include Runtime::Fixtures
@@ -33,7 +33,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = package_name
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index c4bfaacca11..2322d18a9ba 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :reliable do
+ RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
describe 'npm registry' do
include Runtime::Fixtures
@@ -50,7 +50,7 @@ module QA
stages:
- deploy
-
+
deploy:
stage: deploy
script:
@@ -72,7 +72,7 @@ module QA
stages:
- install
-
+
install:
stage: install
script:
@@ -120,7 +120,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = "@#{registry_scope}/#{project.name}"
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
index daf41f1c6ab..1f62b285798 100644
--- a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
@@ -3,7 +3,7 @@
require 'securerandom'
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'NuGet Repository' do
include Runtime::Fixtures
let(:project) do
@@ -14,7 +14,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = "dotnetcore-#{SecureRandom.hex(8)}"
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/online_garbage_collection_spec.rb b/qa/qa/specs/features/browser_ui/5_package/online_garbage_collection_spec.rb
new file mode 100644
index 00000000000..65fc12545b7
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/online_garbage_collection_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package' do
+ describe 'Container Registry Online Garbage Collection', :registry_gc, only: { subdomain: %i[pre] } do
+ let(:group) { Resource::Group.fabricate_via_api! }
+
+ let(:imported_project) do
+ Resource::ProjectImportedFromURL.fabricate_via_browser_ui! do |project|
+ project.name = 'container-registry'
+ project.group = group
+ project.gitlab_repository_path = 'https://gitlab.com/gitlab-org/container-registry.git'
+ end
+ end
+
+ let!(:gitlab_ci_yaml) do
+ <<~YAML
+ variables:
+ GOPATH: $CI_PROJECT_DIR/.go
+ BUILD_CACHE: $CI_PROJECT_DIR/.online-gc-tester
+ STAGE_ONE_VALIDATION_DELAY: "6m"
+ STAGE_TWO_VALIDATION_DELAY: "12m"
+ STAGE_THREE_VALIDATION_DELAY: "6m"
+ STAGE_FOUR_VALIDATION_DELAY: "12m"
+ STAGE_FIVE_VALIDATION_DELAY: "12m"
+
+ stages:
+ - generate
+ - build
+ - test
+
+ .base: &base
+ image: docker:19
+ services:
+ - docker:19-dind
+ variables:
+ DOCKER_HOST: tcp://docker:2376
+ DOCKER_TLS_CERTDIR: "/certs"
+ DOCKER_TLS_VERIFY: 1
+ DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
+ before_script:
+ - until docker info; do sleep 1; done
+ - mkdir -p $GOPATH
+ - mkdir -p $BUILD_CACHE
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+
+ test:
+ stage: generate
+ extends: .base
+ script:
+ - apk add go make git
+ - make binaries
+ - ./bin/online-gc-tester generate --base-dir=$BUILD_CACHE
+ - ./bin/online-gc-tester build --base-dir=$BUILD_CACHE
+ - ./bin/online-gc-tester push --base-dir=$BUILD_CACHE
+ - ./bin/online-gc-tester pull --base-dir=$BUILD_CACHE
+ - ./bin/online-gc-tester test --base-dir=$BUILD_CACHE --stage=1 --delay=$STAGE_ONE_VALIDATION_DELAY
+ - ./bin/online-gc-tester test --base-dir=$BUILD_CACHE --stage=2 --delay=$STAGE_TWO_VALIDATION_DELAY
+ - ./bin/online-gc-tester test --base-dir=$BUILD_CACHE --stage=3 --delay=$STAGE_THREE_VALIDATION_DELAY
+ - ./bin/online-gc-tester test --base-dir=$BUILD_CACHE --stage=4 --delay=$STAGE_FOUR_VALIDATION_DELAY
+ - ./bin/online-gc-tester test --base-dir=$BUILD_CACHE --stage=5 --delay=$STAGE_FIVE_VALIDATION_DELAY
+ timeout: 1h 30m
+ YAML
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ imported_project
+
+ Page::Project::Menu.perform(&:go_to_repository_settings)
+
+ Page::Project::Settings::Repository.perform do |setting|
+ setting.expand_default_branch
+ end
+
+ Page::Project::Settings::DefaultBranch.perform do |setting|
+ setting.set_default_branch('online-gc-test-builder-poc')
+ setting.click_save_changes_button
+ end
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = imported_project
+ commit.branch = 'online-gc-test-builder-poc'
+ commit.commit_message = 'Update .gitlab-ci.yml'
+ commit.update_files([{
+ file_path: '.gitlab-ci.yml',
+ content: gitlab_ci_yaml
+ }])
+ end
+ end
+
+ it 'runs the online garbage collector tool', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1854' do
+ imported_project.visit!
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('test')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 3900)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
index fb29af43da6..a9034174cab 100644
--- a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
describe 'PyPI Repository' do
include Runtime::Fixtures
let(:project) do
@@ -11,7 +11,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = 'mypypipackage'
package.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
index 5d15885cd67..530a3243766 100644
--- a/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :requires_admin, :packages do
+ RSpec.describe 'Package', :orchestrated, :requires_admin, :packages, :object_storage do
describe 'RubyGems Repository' do
include Runtime::Fixtures
@@ -12,7 +12,7 @@ module QA
end
let(:package) do
- Resource::Package.new.tap do |package|
+ Resource::Package.init do |package|
package.name = 'mygem'
package.project = project
end
diff --git a/qa/qa/specs/helpers/context_selector.rb b/qa/qa/specs/helpers/context_selector.rb
index 4313f7c34dd..40ecb9b3506 100644
--- a/qa/qa/specs/helpers/context_selector.rb
+++ b/qa/qa/specs/helpers/context_selector.rb
@@ -13,15 +13,17 @@ module QA
config.before do |example|
if example.metadata.key?(:only)
skip('Test is not compatible with this environment or pipeline') unless ContextSelector.context_matches?(example.metadata[:only])
- elsif example.metadata.key?(:exclude)
- skip('Test is excluded in this job') if ContextSelector.exclude?(example.metadata[:exclude])
+ elsif example.metadata.key?(:except)
+ skip('Test is excluded in this job') if ContextSelector.except?(example.metadata[:except])
end
end
end
end
- def exclude?(*options)
- return false unless Runtime::Env.ci_job_name.present?
+ def except?(*options)
+ return false if Runtime::Env.ci_job_name.blank? && options.any? { |o| o.is_a?(Hash) && o[:job].present? }
+ return false if Runtime::Env.ci_project_name.blank? && options.any? { |o| o.is_a?(Hash) && o[:pipeline].present? }
+ return false if Runtime::Scenario.attributes[:gitlab_address].blank?
context_matches?(*options)
end
@@ -40,10 +42,14 @@ module QA
next unless option.is_a?(Hash)
- if option[:pipeline].present? && Runtime::Env.ci_project_name.present?
+ if option[:pipeline].present?
+ return true if Runtime::Env.ci_project_name.blank?
+
return pipeline_matches?(option[:pipeline])
elsif option[:job].present?
+ return true if Runtime::Env.ci_job_name.blank?
+
return job_matches?(option[:job])
elsif option[:subdomain].present?
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index ff690962db8..bd9907611c7 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -65,6 +65,8 @@ module QA
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
end
+ Runtime::Scenario.define(:large_setup?, args.flatten.include?('can_use_large_setup'))
+
if Runtime::Scenario.attributes[:parallel]
ParallelRunner.run(args.flatten)
elsif Runtime::Scenario.attributes[:loop]
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 8218ab428b0..1d702b70d10 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -54,10 +54,10 @@ RSpec.describe QA::Runtime::Env do
default: false
end
- describe '.chrome_headless?' do
+ describe '.webdriver_headless?' do
it_behaves_like 'boolean method',
- method: :chrome_headless?,
- env_key: 'CHROME_HEADLESS',
+ method: :webdriver_headless?,
+ env_key: 'WEBDRIVER_HEADLESS',
default: true
end
diff --git a/qa/spec/service/docker_run/gitlab_runner_spec.rb b/qa/spec/service/docker_run/gitlab_runner_spec.rb
index 34d95943321..a8838db10cf 100644
--- a/qa/spec/service/docker_run/gitlab_runner_spec.rb
+++ b/qa/spec/service/docker_run/gitlab_runner_spec.rb
@@ -130,7 +130,7 @@ module QA
end
it 'mounts the docker socket to the host runner' do
- expect(subject).to have_received(:shell).with(/-v \/var\/run\/docker.sock:\/var\/run\/docker.sock /)
+ expect(subject).to have_received(:shell).with(%r{-v /var/run/docker.sock:/var/run/docker.sock })
end
it 'runs in privileged mode' do
diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb
index 7792d33dcf9..f0250103008 100644
--- a/qa/spec/specs/helpers/context_selector_spec.rb
+++ b/qa/spec/specs/helpers/context_selector_spec.rb
@@ -49,8 +49,10 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
it 'matches multiple subdomains' do
QA::Runtime::Scenario.define(:gitlab_address, "https://staging.gitlab.com")
- expect(described_class.context_matches?(subdomain: [:release, :staging])).to be_truthy
- expect(described_class.context_matches?(:production, subdomain: [:release, :staging])).to be_truthy
+ aggregate_failures do
+ expect(described_class.context_matches?(subdomain: [:release, :staging])).to be_truthy
+ expect(described_class.context_matches?(:production, subdomain: [:release, :staging])).to be_truthy
+ end
end
it 'matches :production' do
@@ -87,6 +89,16 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(group.examples[0].execution_result.status).to eq(:passed)
end
+
+ context 'when excluding contexts' do
+ it 'can apply to contexts or descriptions' do
+ group = describe_successfully 'skips staging', except: { subdomain: :staging } do
+ it('skips staging') {}
+ end
+
+ expect(group.examples[0].execution_result.status).to eq(:pending)
+ end
+ end
end
context 'with different environment set' do
@@ -102,6 +114,16 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(group.examples[0].execution_result.status).to eq(:pending)
end
+
+ context 'when excluding contexts' do
+ it 'runs against production' do
+ group = describe_successfully 'Runs in staging', :something, except: { subdomain: :staging } do
+ it('runs in staging') {}
+ end
+
+ expect(group.examples[0].execution_result.status).to eq(:passed)
+ end
+ end
end
end
@@ -113,10 +135,28 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
it('runs in any env') {}
end
- expect(group.examples[0].execution_result.status).to eq(:passed)
- expect(group.examples[1].execution_result.status).to eq(:pending)
- expect(group.examples[2].execution_result.status).to eq(:passed)
- expect(group.examples[3].execution_result.status).to eq(:passed)
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:passed)
+ expect(group.examples[1].execution_result.status).to eq(:pending)
+ expect(group.examples[2].execution_result.status).to eq(:passed)
+ expect(group.examples[3].execution_result.status).to eq(:passed)
+ end
+ end
+
+ context 'when excluding contexts' do
+ it 'skips staging' do
+ group = describe_successfully do
+ it('skips staging', except: { subdomain: :staging }) {}
+ it('runs in staging', except: :production) {}
+ it('skips staging also', except: { subdomain: %i[release staging] }) {}
+ end
+
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:pending)
+ expect(group.examples[1].execution_result.status).to eq(:passed)
+ expect(group.examples[2].execution_result.status).to eq(:pending)
+ end
+ end
end
context 'custom env' do
@@ -130,8 +170,24 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
it('does not run on release', only: :production) {}
end
- expect(group.examples.first.execution_result.status).to eq(:passed)
- expect(group.examples.last.execution_result.status).to eq(:pending)
+ aggregate_failures do
+ expect(group.examples.first.execution_result.status).to eq(:passed)
+ expect(group.examples.last.execution_result.status).to eq(:pending)
+ end
+ end
+
+ context 'when excluding contexts' do
+ it 'skips a custom environment' do
+ group = describe_successfully do
+ it('skips release gitlab net', except: { tld: '.net', subdomain: :release, domain: 'gitlab' }) {}
+ it('runs on release', except: :production) {}
+ end
+
+ aggregate_failures do
+ expect(group.examples.first.execution_result.status).to eq(:pending)
+ expect(group.examples.last.execution_result.status).to eq(:passed)
+ end
+ end
end
end
@@ -147,9 +203,27 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
it('runs in prod and staging', only: { subdomain: /(staging.)?/, domain: 'gitlab' }) {}
end
- expect(group.examples[0].execution_result.status).to eq(:passed)
- expect(group.examples[1].execution_result.status).to eq(:pending)
- expect(group.examples[2].execution_result.status).to eq(:passed)
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:passed)
+ expect(group.examples[1].execution_result.status).to eq(:pending)
+ expect(group.examples[2].execution_result.status).to eq(:passed)
+ end
+ end
+
+ context 'when excluding contexts' do
+ it 'skips production' do
+ group = describe_successfully do
+ it('skips prod', except: :production) {}
+ it('runs on prod', except: { subdomain: :staging }) {}
+ it('skips prod and staging', except: { subdomain: /(staging.)?/, domain: 'gitlab' }) {}
+ end
+
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:pending)
+ expect(group.examples[1].execution_result.status).to eq(:passed)
+ expect(group.examples[2].execution_result.status).to eq(:pending)
+ end
+ end
end
end
@@ -179,6 +253,21 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(group.examples[1].execution_result.status).to eq(:passed)
end
end
+
+ context 'when excluding contexts' do
+ it 'runs in any pipeline' do
+ group = describe_successfully do
+ it('runs given a single named pipeline', except: { pipeline: :nightly }) {}
+ it('runs given an array of pipelines', except: { pipeline: [:canary, :not_nightly] }) {}
+ end
+
+ aggregate_failures do
+ group.examples.each do |example|
+ expect(example.execution_result.status).to eq(:passed)
+ end
+ end
+ end
+ end
end
context 'when a pipeline triggered from the default branch runs in gitlab-qa' do
@@ -200,6 +289,22 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(group.examples[2].execution_result.status).to eq(:pending)
end
end
+
+ context 'when excluding contexts' do
+ it 'skips default branch pipelines' do
+ group = describe_successfully do
+ it('skips main pipeline given a single pipeline', except: { pipeline: :main }) {}
+ it('skips main given an array of pipelines', except: { pipeline: [:canary, :main] }) {}
+ it('runs non-default pipelines', except: { pipeline: [:nightly, :not_nightly, :not_main] }) {}
+ end
+
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:pending)
+ expect(group.examples[1].execution_result.status).to eq(:pending)
+ expect(group.examples[2].execution_result.status).to eq(:passed)
+ end
+ end
+ end
end
context 'with CI_PROJECT_NAME set' do
@@ -223,24 +328,42 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(group.examples[3].execution_result.status).to eq(:pending)
end
end
+
+ context 'when excluding contexts' do
+ it 'skips designated pipeline' do
+ group = describe_successfully do
+ it('skips nightly', except: { pipeline: :nightly }) {}
+ it('runs in not_nightly', except: { pipeline: :not_nightly }) {}
+ it('skips on nightly given an array', except: { pipeline: [:canary, :nightly] }) {}
+ it('runs in not_nightly given an array', except: { pipeline: [:not_nightly, :canary] }) {}
+ end
+
+ aggregate_failures do
+ expect(group.examples[0].execution_result.status).to eq(:pending)
+ expect(group.examples[1].execution_result.status).to eq(:passed)
+ expect(group.examples[2].execution_result.status).to eq(:pending)
+ expect(group.examples[3].execution_result.status).to eq(:passed)
+ end
+ end
+ end
end
end
- context 'when excluding contexts' do
- context 'with job constraints' do
- context 'without CI_JOB_NAME set' do
- before do
- stub_env('CI_JOB_NAME', nil)
- described_class.configure_rspec
- end
+ context 'with job constraints' do
+ context 'without CI_JOB_NAME set' do
+ before do
+ stub_env('CI_JOB_NAME', nil)
+ described_class.configure_rspec
+ end
+ context 'when excluding contexts' do
it 'runs in any job' do
group = describe_successfully do
- it('runs given a single named job', exclude: { job: 'ee:instance-image' }) {}
- it('runs given a single regex pattern', exclude: { job: '.*:instance-image' }) {}
- it('runs given an array of jobs', exclude: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
- it('runs given an array of regex patterns', exclude: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
- it('runs given a mix of strings and regex patterns', exclude: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
+ it('runs given a single named job', except: { job: 'ee:instance-image' }) {}
+ it('runs given a single regex pattern', except: { job: '.*:instance-image' }) {}
+ it('runs given an array of jobs', except: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('runs given an array of regex patterns', except: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
+ it('runs given a mix of strings and regex patterns', except: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
end
aggregate_failures do
@@ -251,19 +374,39 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
end
end
- context 'with CI_JOB_NAME set' do
- before do
- stub_env('CI_JOB_NAME', 'ee:instance-image')
- described_class.configure_rspec
+ context 'when including only specific contexts' do
+ it 'runs in any job' do
+ group = describe_successfully do
+ it('runs given a single named job', only: { job: 'ee:instance-image' }) {}
+ it('runs given a single regex pattern', only: { job: '.*:instance-image' }) {}
+ it('runs given an array of jobs', only: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('runs given an array of regex patterns', only: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
+ it('runs given a mix of strings and regex patterns', only: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
+ end
+
+ aggregate_failures do
+ group.examples.each do |example|
+ expect(example.execution_result.status).to eq(:passed)
+ end
+ end
end
+ end
+ end
+
+ context 'with CI_JOB_NAME set' do
+ before do
+ stub_env('CI_JOB_NAME', 'ee:instance-image')
+ described_class.configure_rspec
+ end
+ context 'when excluding contexts' do
it 'does not run in the specified job' do
group = describe_successfully do
- it('skips given a single named job', exclude: { job: 'ee:instance-image' }) {}
- it('skips given a single regex pattern', exclude: { job: '.*:instance-image' }) {}
- it('skips given an array of jobs', exclude: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
- it('skips given an array of regex patterns', exclude: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
- it('skips given a mix of strings and regex patterns', exclude: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
+ it('skips given a single named job', except: { job: 'ee:instance-image' }) {}
+ it('skips given a single regex pattern', except: { job: '.*:instance-image' }) {}
+ it('skips given an array of jobs', except: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('skips given an array of regex patterns', except: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
+ it('skips given a mix of strings and regex patterns', except: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
end
aggregate_failures do
@@ -275,11 +418,11 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
it 'runs in jobs that do not match' do
group = describe_successfully do
- it('runs given a single named job', exclude: { job: 'ce:instance-image' }) {}
- it('runs given a single regex pattern', exclude: { job: '.*:instance-image-quarantine' }) {}
- it('runs given an array of jobs', exclude: { job: %w[ce:instance-image qa-schedules-browser_ui-3_create] }) {}
- it('runs given an array of regex patterns', exclude: { job: %w[ce:.* qa-schedules-browser_ui.*] }) {}
- it('runs given a mix of strings and regex patterns', exclude: { job: %w[ce:instance-image qa-schedules-browser_ui.*] }) {}
+ it('runs given a single named job', except: { job: 'ce:instance-image' }) {}
+ it('runs given a single regex pattern', except: { job: '.*:instance-image-quarantine' }) {}
+ it('runs given an array of jobs', except: { job: %w[ce:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('runs given an array of regex patterns', except: { job: %w[ce:.* qa-schedules-browser_ui.*] }) {}
+ it('runs given a mix of strings and regex patterns', except: { job: %w[ce:instance-image qa-schedules-browser_ui.*] }) {}
end
aggregate_failures do
@@ -289,6 +432,40 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
end
end
end
+
+ context 'when including only specific contexts' do
+ it 'runs only in the specified jobs' do
+ group = describe_successfully do
+ it('runs given a single named job', only: { job: 'ee:instance-image' }) {}
+ it('runs given a single regex pattern', only: { job: '.*:instance-image' }) {}
+ it('runs given an array of jobs', only: { job: %w[ee:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('runs given an array of regex patterns', only: { job: %w[ee:.* qa-schedules-browser_ui.*] }) {}
+ it('runs given a mix of strings and regex patterns', only: { job: %w[ee:instance-image qa-schedules-browser_ui.*] }) {}
+ end
+
+ aggregate_failures do
+ group.examples.each do |example|
+ expect(example.execution_result.status).to eq(:passed)
+ end
+ end
+ end
+
+ it 'does not run in jobs that do not match' do
+ group = describe_successfully do
+ it('skips given a single named job', only: { job: 'ce:instance-image' }) {}
+ it('skips given a single regex pattern', only: { job: '.*:instance-image-quarantine' }) {}
+ it('skips given an array of jobs', only: { job: %w[ce:instance-image qa-schedules-browser_ui-3_create] }) {}
+ it('skips given an array of regex patterns', only: { job: %w[ce:.* qa-schedules-browser_ui.*] }) {}
+ it('skips given a mix of strings and regex patterns', only: { job: %w[ce:instance-image qa-schedules-browser_ui.*] }) {}
+ end
+
+ aggregate_failures do
+ group.examples.each do |example|
+ expect(example.execution_result.status).to eq(:pending)
+ end
+ end
+ end
+ end
end
end
end
diff --git a/qa/tasks/webdrivers.rake b/qa/tasks/webdrivers.rake
new file mode 100644
index 00000000000..f4fa3fab555
--- /dev/null
+++ b/qa/tasks/webdrivers.rake
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require 'webdrivers'
+load 'webdrivers/Rakefile'