summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /qa
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
downloadgitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'qa')
-rw-r--r--qa/.rspec4
-rw-r--r--qa/.rspec_internal2
-rw-r--r--qa/Dockerfile62
-rw-r--r--qa/README.md2
-rw-r--r--qa/Rakefile5
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.rb27
-rw-r--r--qa/lib/gitlab/page/group/settings/billing.stub.rb216
-rw-r--r--qa/lib/gitlab/page/group/settings/usage_quotas.rb2
-rw-r--r--qa/qa.rb6
-rw-r--r--qa/qa/ce/strategy.rb1
-rw-r--r--qa/qa/fixtures/package_managers/maven/build_install.gradle.erb1
-rw-r--r--qa/qa/fixtures/web_ide/logo_sample.svg29
-rw-r--r--qa/qa/flow/merge_request.rb20
-rw-r--r--qa/qa/git/repository.rb34
-rw-r--r--qa/qa/page/base.rb19
-rw-r--r--qa/qa/page/component/blob_content.rb11
-rw-r--r--qa/qa/page/component/new_snippet.rb3
-rw-r--r--qa/qa/page/component/snippet.rb4
-rw-r--r--qa/qa/page/dashboard/snippet/edit.rb3
-rw-r--r--qa/qa/page/file/form.rb3
-rw-r--r--qa/qa/page/group/settings/group_deploy_tokens.rb8
-rw-r--r--qa/qa/page/main/login.rb12
-rw-r--r--qa/qa/page/merge_request/index.rb17
-rw-r--r--qa/qa/page/merge_request/new.rb15
-rw-r--r--qa/qa/page/merge_request/show.rb9
-rw-r--r--qa/qa/page/project/pipeline/new.rb4
-rw-r--r--qa/qa/page/project/pipeline/show.rb6
-rw-r--r--qa/qa/page/project/pipeline_editor/show.rb30
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb7
-rw-r--r--qa/qa/page/project/settings/deploy_tokens.rb24
-rw-r--r--qa/qa/page/project/settings/general_pipelines.rb23
-rw-r--r--qa/qa/page/project/settings/merge_request.rb2
-rw-r--r--qa/qa/page/project/settings/services/prometheus.rb2
-rw-r--r--qa/qa/page/project/show.rb6
-rw-r--r--qa/qa/resource/group_base.rb4
-rw-r--r--qa/qa/resource/group_deploy_token.rb4
-rw-r--r--qa/qa/resource/job.rb35
-rw-r--r--qa/qa/resource/members.rb3
-rw-r--r--qa/qa/resource/merge_request.rb12
-rw-r--r--qa/qa/resource/pipeline.rb50
-rw-r--r--qa/qa/resource/project.rb38
-rw-r--r--qa/qa/resource/project_access_token.rb4
-rw-r--r--qa/qa/resource/project_deploy_token.rb4
-rw-r--r--qa/qa/resource/project_imported_from_github.rb2
-rw-r--r--qa/qa/resource/repository/commit.rb6
-rw-r--r--qa/qa/runtime/browser.rb31
-rw-r--r--qa/qa/runtime/env.rb6
-rw-r--r--qa/qa/runtime/feature.rb2
-rw-r--r--qa/qa/runtime/ip_address.rb2
-rw-r--r--qa/qa/scenario/template.rb63
-rw-r--r--qa/qa/scenario/test/integration/mattermost.rb7
-rw-r--r--qa/qa/service/cluster_provider/k3s_cilium.rb93
-rw-r--r--qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb71
-rw-r--r--qa/qa/specs/features/api/1_manage/project_access_token_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb5
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb166
-rw-r--r--qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb120
-rw-r--r--qa/qa/specs/features/api/5_package/container_registry_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb24
-rw-r--r--qa/qa/specs/features/browser_ui/14_non_devops/service_ping_disabled_spec.rb24
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_from_push_notification_spec.rb64
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb6
-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/suggestions/batch_suggestion_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb40
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb46
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb88
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/multi-project_pipelines_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_dependent_relationship_spec.rb23
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_can_create_merge_request_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb1
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb266
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb240
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb7
-rw-r--r--qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb (renamed from qa/spec/support/shared_contexts/merge_train_spec_with_user_prep.rb)0
-rw-r--r--qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb (renamed from qa/spec/support/shared_contexts/packages_registry_shared_context.rb)0
-rw-r--r--qa/qa/specs/features/shared_contexts/variable_inheritance_shared_context.rb (renamed from qa/spec/support/shared_contexts/variable_inheritance_shared_context.rb)8
-rw-r--r--qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb (renamed from qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb)0
-rw-r--r--qa/qa/specs/knapsack_runner.rb21
-rw-r--r--qa/qa/specs/qa_deprecation_toolkit_env.rb (renamed from qa/spec/qa_deprecation_toolkit_env.rb)2
-rw-r--r--qa/qa/specs/runner.rb98
-rw-r--r--qa/qa/specs/spec_helper.rb136
-rw-r--r--qa/qa/support/formatters/allure_metadata_formatter.rb35
-rw-r--r--qa/qa/support/gitlab_address.rb48
-rw-r--r--qa/qa/support/knapsack_report.rb (renamed from qa/qa/tools/knapsack_report.rb)31
-rw-r--r--qa/qa/support/matchers/have_matcher.rb1
-rw-r--r--qa/qa/support/page/logging.rb6
-rw-r--r--qa/qa/tools/delete_test_snippets.rb78
-rw-r--r--qa/spec/README.md7
-rw-r--r--qa/spec/git/repository_spec.rb6
-rw-r--r--qa/spec/scenario/template_spec.rb72
-rw-r--r--qa/spec/scenario/test/integration/mattermost_spec.rb18
-rw-r--r--qa/spec/scenario_shared_examples.rb (renamed from qa/spec/specs/scenario_shared_examples.rb)19
-rw-r--r--qa/spec/spec_helper.rb132
-rw-r--r--qa/spec/specs/spec_helper.rb5
-rw-r--r--qa/tasks/knapsack.rake22
-rw-r--r--qa/tasks/vulnerabilities.rake29
116 files changed, 1977 insertions, 1072 deletions
diff --git a/qa/.rspec b/qa/.rspec
index b83d9b7aa65..68d481ca4d9 100644
--- a/qa/.rspec
+++ b/qa/.rspec
@@ -1,3 +1,5 @@
---color
+--force-color
+--order random
--format documentation
+--default-path qa/specs
--require spec_helper
diff --git a/qa/.rspec_internal b/qa/.rspec_internal
index ea32ca1e093..75786e123d4 100644
--- a/qa/.rspec_internal
+++ b/qa/.rspec_internal
@@ -1,4 +1,4 @@
--force-color
--order random
--format documentation
---require specs/spec_helper
+--require spec_helper
diff --git a/qa/Dockerfile b/qa/Dockerfile
index fa666daa927..4fd44ba02df 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,33 +1,17 @@
-FROM ruby:2.7-buster
+FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
-ENV DEBIAN_FRONTEND="noninteractive"
-ENV DOCKER_VERSION="17.09.0-ce"
+ENV DEBIAN_FRONTEND="noninteractive" \
+ BUNDLE_WITHOUT=development
##
-# Update APT sources and install dependencies
+# Install system libs
#
-RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list
-RUN apt-get update && apt-get install -y wget unzip xvfb lsb-release git git-lfs
-
-##
-# Install Docker
-#
-RUN wget -q "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" && \
- tar -zxf "docker-${DOCKER_VERSION}.tgz" && mv docker/docker /usr/local/bin/docker && \
- rm "docker-${DOCKER_VERSION}.tgz"
-
-##
-# Install client certificate - Bug in Chrome Headless: https://gitlab.com/gitlab-org/gitlab/-/issues/331492
-#
-# RUN apt install -y libnss3-tools
-# RUN mkdir -p $HOME/.pki/nssdb
-# RUN certutil -N -d sql:$HOME/.pki/nssdb
-# COPY ./qa/tls_certificates/client/client.pfx /tmp/client.pfx
-# RUN pk12util -d sql:$HOME/.pki/nssdb -i /tmp/client.pfx -W ''
-# RUN mkdir -p /etc/opt/chrome/policies/managed
-# RUN echo '{ "AutoSelectCertificateForUrls": ["{\"pattern\":\"*\",\"filter\":{}}"] }' > /etc/opt/chrome/policies/managed/policy.json
-# RUN cat /etc/opt/chrome/policies/managed/policy.json
+RUN apt-get update; \
+ apt-get install -y xvfb unzip; \
+ apt-get -yq autoremove; \
+ apt-get clean -yqq; \
+ rm -rf /var/lib/apt/lists/*
##
# Install root certificate
@@ -37,39 +21,19 @@ ADD ./qa/tls_certificates/authority/ca.crt /usr/share/ca-certificates/gitlab/
RUN echo 'gitlab/ca.crt' >> /etc/ca-certificates.conf
RUN chmod -R 644 /usr/share/ca-certificates/gitlab && update-ca-certificates
-##
-# Install gcloud and kubectl CLI used in Auto DevOps test to create K8s
-# clusters
-#
-RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
- echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
- 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 gem install bundler --no-document --conservative --version 2.3.6
-RUN bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
+RUN bundle install --jobs=$(nproc) --retry=3
##
# 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
diff --git a/qa/README.md b/qa/README.md
index 2e3f6af5bc3..724638d13c0 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -197,7 +197,7 @@ another test has `:ldap` and `:quarantine` metadata. If the tests are run with
### Running tests with a feature flag enabled or disabled
-Tests can be run with with a feature flag enabled or disabled by using the command-line
+Tests can be run with a feature flag enabled or disabled by using the command-line
option `--enable-feature FEATURE_FLAG` or `--disable-feature FEATURE_FLAG`.
For example, to enable the feature flag that enforces Gitaly request limits,
diff --git a/qa/Rakefile b/qa/Rakefile
index 0a65a58e6fc..e4d38d8294f 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -65,6 +65,11 @@ task :delete_test_users, [:delete_before, :dry_run, :exclude_users] do |t, args|
QA::Tools::DeleteTestUsers.new(args).run
end
+desc "Deletes snippets"
+task :delete_test_snippets, [:delete_before, :dry_run] do |t, args|
+ QA::Tools::DeleteTestSnippets.new(args).run
+end
+
namespace :test_resources do
desc "Deletes resources created during E2E test runs"
task :delete, [:file_pattern] do |t, args|
diff --git a/qa/lib/gitlab/page/group/settings/billing.rb b/qa/lib/gitlab/page/group/settings/billing.rb
index 24d327502f8..d0d73278890 100644
--- a/qa/lib/gitlab/page/group/settings/billing.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.rb
@@ -5,13 +5,30 @@ module Gitlab
module Group
module Settings
class Billing < Chemlab::Page
- # TODO: Supplant with data-qa-selectors
- h4 :billing_plan_header, css: 'div.billing-plan-header h4'
-
+ h4 :billing_plan_header
link :start_your_free_trial
+ link :upgrade_to_premium
+ link :upgrade_to_ultimate
+
+ # Subscription details
+ strong :subscription_header
+ button :refresh_seats
+
+ # Usage
+ p :seats_in_subscription
+ p :seats_currently_in_use
+ link :see_seats_usage
+ p :max_seats_used
+ p :seats_owed
+
+ # Billing
+ p :subscription_start_date
+ p :subscription_end_date
- link :upgrade_to_premium, css: '[data-testid="plan-card-premium"] a.billing-cta-purchase-new'
- link :upgrade_to_ultimate, css: '[data-testid="plan-card-ultimate"] a.billing-cta-purchase-new'
+ def refresh_subscription_seats
+ refresh_seats
+ ::QA::Support::WaitForRequests.wait_for_requests
+ end
end
end
end
diff --git a/qa/lib/gitlab/page/group/settings/billing.stub.rb b/qa/lib/gitlab/page/group/settings/billing.stub.rb
index 64176af794a..c49d744d61f 100644
--- a/qa/lib/gitlab/page/group/settings/billing.stub.rb
+++ b/qa/lib/gitlab/page/group/settings/billing.stub.rb
@@ -100,6 +100,222 @@ module Gitlab
def upgrade_to_ultimate?
# This is a stub, used for indexing. The method is dynamically generated.
end
+
+ # @note Defined as +strong :subscription_header+
+ # @return [String] The text content or value of +subscription_header+
+ def subscription_header
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_header_element).to exist
+ # end
+ # @return [Watir::Strong] The raw +Strong+ element
+ def subscription_header_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_header
+ # end
+ # @return [Boolean] true if the +subscription_header+ element is present on the page
+ def subscription_header?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +button :refresh_seats+
+ # Clicks +refresh_seats+
+ def refresh_seats
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.refresh_seats_element).to exist
+ # end
+ # @return [Watir::Button] The raw +Button+ element
+ def refresh_seats_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_refresh_seats
+ # end
+ # @return [Boolean] true if the +refresh_seats+ element is present on the page
+ def refresh_seats?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_in_subscription+
+ # @return [String] The text content or value of +seats_in_subscription+
+ def seats_in_subscription
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_in_subscription_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_in_subscription_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_in_subscription
+ # end
+ # @return [Boolean] true if the +seats_in_subscription+ element is present on the page
+ def seats_in_subscription?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_currently_in_use+
+ # @return [String] The text content or value of +seats_currently_in_use+
+ def seats_currently_in_use
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_currently_in_use_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_currently_in_use_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_currently_in_use
+ # end
+ # @return [Boolean] true if the +seats_currently_in_use+ element is present on the page
+ def seats_currently_in_use?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +link :see_seats_usage+
+ # Clicks +see_seats_usage+
+ def see_seats_usage
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.see_seats_usage_element).to exist
+ # end
+ # @return [Watir::Link] The raw +Link+ element
+ def see_seats_usage_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_see_seats_usage
+ # end
+ # @return [Boolean] true if the +see_seats_usage+ element is present on the page
+ def see_seats_usage?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :max_seats_used+
+ # @return [String] The text content or value of +max_seats_used+
+ def max_seats_used
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.max_seats_used_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def max_seats_used_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_max_seats_used
+ # end
+ # @return [Boolean] true if the +max_seats_used+ element is present on the page
+ def max_seats_used?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :seats_owed+
+ # @return [String] The text content or value of +seats_owed+
+ def seats_owed
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.seats_owed_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def seats_owed_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_seats_owed
+ # end
+ # @return [Boolean] true if the +seats_owed+ element is present on the page
+ def seats_owed?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :subscription_start_date+
+ # @return [String] The text content or value of +subscription_start_date+
+ def subscription_start_date
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_start_date_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def subscription_start_date_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_start_date
+ # end
+ # @return [Boolean] true if the +subscription_start_date+ element is present on the page
+ def subscription_start_date?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +p :subscription_end_date+
+ # @return [String] The text content or value of +subscription_end_date+
+ def subscription_end_date
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing.subscription_end_date_element).to exist
+ # end
+ # @return [Watir::P] The raw +P+ element
+ def subscription_end_date_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Group::Settings::Billing.perform do |billing|
+ # expect(billing).to be_subscription_end_date
+ # end
+ # @return [Boolean] true if the +subscription_end_date+ element is present on the page
+ def subscription_end_date?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
end
end
end
diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
index 0408668a81c..df12fe4076c 100644
--- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb
+++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
@@ -14,7 +14,7 @@ module Gitlab
span :purchased_usage_total
div :ci_purchase_successful_alert, text: /You have successfully purchased CI minutes/
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
- h4 :storage_available_alert, text: /purchased storage is available/
+ h2 :storage_available_alert, text: /purchased storage is available/
def plan_ci_limits
plan_ci_minutes_element.span.text[%r{([^/ ]+)$}]
diff --git a/qa/qa.rb b/qa/qa.rb
index 73bcb6de637..8d358aa2efe 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -24,6 +24,7 @@ module QA
loader.push_dir(root, namespace: QA)
loader.ignore("#{root}/specs/features")
+ loader.ignore("#{root}/specs/spec_helper.rb")
loader.inflector.inflect(
"ce" => "CE",
@@ -63,5 +64,10 @@ module QA
"registry_with_cdn" => "RegistryWithCDN"
)
+ # Configure knapsack at the very begining of the setup
+ loader.on_setup do
+ QA::Support::KnapsackReport.configure!
+ end
+
loader.setup
end
diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb
index 71c538c20a0..bf08f887c7d 100644
--- a/qa/qa/ce/strategy.rb
+++ b/qa/qa/ce/strategy.rb
@@ -8,6 +8,7 @@ module QA
def perform_before_hooks
# The login page could take some time to load the first time it is visited.
# We visit the login page and wait for it to properly load only once before the tests.
+ QA::Runtime::Logger.info("Performing sanity check for environment!")
QA::Support::Retrier.retry_on_exception do
QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login)
end
diff --git a/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb b/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb
index 303a64ad233..31543d30e88 100644
--- a/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb
+++ b/qa/qa/fixtures/package_managers/maven/build_install.gradle.erb
@@ -4,7 +4,6 @@ plugins {
}
repositories {
- jcenter()
maven {
url "<%= gitlab_address_with_port %>/api/v4/projects/<%= package_project.id %>/packages/maven"
name "GitLab"
diff --git a/qa/qa/fixtures/web_ide/logo_sample.svg b/qa/qa/fixtures/web_ide/logo_sample.svg
index 883e7e6cf92..211b511c8c7 100644
--- a/qa/qa/fixtures/web_ide/logo_sample.svg
+++ b/qa/qa/fixtures/web_ide/logo_sample.svg
@@ -1,27 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+<svg width="50" height="48" viewBox="0 0 50 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<script>alert('FAIL')</script>
<defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
- <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
- <g id="Page-1" sketch:type="MSShapeGroup">
- <g id="Fill-1-+-Group-24">
- <g id="Group-24">
- <g id="Group">
- <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path>
- <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path>
- <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path>
- <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path>
- <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path>
- <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path>
- <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path>
- </g>
- </g>
- </g>
- </g>
- </g>
- </g>
+ <path d="m49.014 19-.067-.18-6.784-17.696a1.792 1.792 0 0 0-3.389.182l-4.579 14.02H15.651l-4.58-14.02a1.795 1.795 0 0 0-3.388-.182l-6.78 17.7-.071.175A12.595 12.595 0 0 0 5.01 33.556l.026.02.057.044 10.32 7.734 5.12 3.87 3.11 2.351a2.102 2.102 0 0 0 2.535 0l3.11-2.352 5.12-3.869 10.394-7.779.029-.022a12.595 12.595 0 0 0 4.182-14.554Z"
+ fill="#E24329"/>
+ <path d="m49.014 19-.067-.18a22.88 22.88 0 0 0-9.12 4.103L24.931 34.187l9.485 7.167 10.393-7.779.03-.022a12.595 12.595 0 0 0 4.175-14.554Z"
+ fill="#FC6D26"/>
+ <path d="m15.414 41.354 5.12 3.87 3.11 2.351a2.102 2.102 0 0 0 2.535 0l3.11-2.352 5.12-3.869-9.484-7.167-9.51 7.167Z"
+ fill="#FCA326"/>
+ <path d="M10.019 22.923a22.86 22.86 0 0 0-9.117-4.1L.832 19A12.595 12.595 0 0 0 5.01 33.556l.026.02.057.044 10.32 7.734 9.491-7.167L10.02 22.923Z"
+ fill="#FC6D26"/>
</svg>
diff --git a/qa/qa/flow/merge_request.rb b/qa/qa/flow/merge_request.rb
index c26140000fe..f1cab2c7d1a 100644
--- a/qa/qa/flow/merge_request.rb
+++ b/qa/qa/flow/merge_request.rb
@@ -10,6 +10,26 @@ module QA
Page::Project::Settings::Main.perform(&:expand_merge_requests_settings)
Page::Project::Settings::MergeRequest.perform(&:enable_merge_train)
end
+
+ # Opens the form to create a new merge request.
+ # It tries to use the "Create merge request" button that appears after
+ # a commit is pushed, but if that button isn't available, it uses the
+ # "New merge request" button on the page that lists merge requests.
+ #
+ # @param [String] source_branch the branch to be merged
+ def create_new(source_branch:)
+ if Page::Project::Show.perform(&:has_create_merge_request_button?)
+ Page::Project::Show.perform(&:new_merge_request)
+ return
+ end
+
+ Page::Project::Menu.perform(&:click_merge_requests)
+ Page::MergeRequest::Index.perform(&:click_new_merge_request)
+ Page::MergeRequest::New.perform do |merge_request|
+ merge_request.select_source_branch(source_branch)
+ merge_request.click_compare_branches_and_continue
+ end
+ end
end
end
end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 356e509a81d..2244a78b9e5 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -21,7 +21,7 @@ module QA
# We set HOME to the current working directory (which is a
# temporary directory created in .perform()) so the temporarily dropped
# .netrc can be utilised
- self.env_vars = [%Q{HOME="#{tmp_home_dir}"}]
+ self.env_vars = [%(HOME="#{tmp_home_dir}")]
@use_lfs = false
@gpg_key_id = nil
@default_branch = Runtime::Env.default_branch
@@ -71,7 +71,7 @@ module QA
def checkout(branch_name, new_branch: false)
opts = new_branch ? '-b' : ''
- run_git(%Q{git checkout #{opts} "#{branch_name}"}).to_s
+ run_git(%(git checkout #{opts} "#{branch_name}")).to_s
end
def shallow_clone
@@ -79,8 +79,8 @@ module QA
end
def configure_identity(name, email)
- run_git(%Q{git config user.name "#{name}"})
- run_git(%Q{git config user.email #{email}})
+ run_git(%(git config user.name "#{name}"))
+ run_git(%(git config user.email #{email}))
end
def commit_file(name, contents, message)
@@ -94,11 +94,11 @@ module QA
::File.write(name, contents)
if use_lfs?
- git_lfs_track_result = run_git(%Q{git lfs track #{name} --lockable})
+ git_lfs_track_result = run_git(%(git lfs track #{name} --lockable))
return git_lfs_track_result.response unless git_lfs_track_result.success?
end
- git_add_result = run_git(%Q{git add #{name}})
+ git_add_result = run_git(%(git add #{name}))
git_lfs_track_result.to_s + git_add_result.to_s
end
@@ -108,15 +108,15 @@ module QA
end
def delete_tag(tag_name)
- run_git(%Q{git push origin --delete #{tag_name}}, max_attempts: 3).to_s
+ run_git(%(git push origin --delete #{tag_name}), max_attempts: 3).to_s
end
def commit(message)
- run_git(%Q{git commit -m "#{message}"}, max_attempts: 3).to_s
+ run_git(%(git commit -m "#{message}"), max_attempts: 3).to_s
end
def commit_with_gpg(message)
- run_git(%Q{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(command -v gpg) && git commit -S -m "#{message}"}).to_s
+ run_git(%{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(command -v gpg) && git commit -S -m "#{message}"}).to_s
end
def current_branch
@@ -159,11 +159,11 @@ module QA
@ssh = Support::SSH.perform do |ssh|
ssh.key = key
ssh.uri = uri
- ssh.setup(env: self.env_vars)
+ ssh.setup(env: env_vars)
ssh
end
- self.env_vars << %Q{GIT_SSH_COMMAND="ssh -i #{ssh.private_key_file.path} -o UserKnownHostsFile=#{ssh.known_hosts_file.path}"}
+ env_vars << %(GIT_SSH_COMMAND="ssh -i #{ssh.private_key_file.path} -o UserKnownHostsFile=#{ssh.known_hosts_file.path}")
end
def delete_ssh_key
@@ -182,7 +182,9 @@ module QA
end
def git_protocol=(value)
- raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
+ unless %w[0 1 2].include?(value.to_s)
+ raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2"
+ end
run_git("git config protocol.version #{value}")
end
@@ -190,8 +192,8 @@ module QA
def fetch_supported_git_protocol
# ls-remote is one command known to respond to Git protocol v2 so we use
# it to get output including the version reported via Git tracing
- result = run_git("git ls-remote #{uri}", max_attempts: 3, env: [*self.env_vars, "GIT_TRACE_PACKET=1"])
- result.response[/git< version (\d+)/, 1] || 'unknown'
+ result = run_git("git ls-remote #{uri}", max_attempts: 3, env: [*env_vars, "GIT_TRACE_PACKET=1"])
+ result.response[/ls-remote< version (\d+)/, 1] || 'unknown'
end
def try_add_credentials_to_netrc
@@ -266,7 +268,7 @@ module QA
#
FileUtils.mkdir_p(tmp_home_dir)
File.open(netrc_file_path, 'a') { |file| file.puts(netrc_content) }
- File.chmod(0600, netrc_file_path)
+ File.chmod(0o600, netrc_file_path)
end
def tmp_home_dir
@@ -302,7 +304,7 @@ module QA
read_netrc_content.grep(/^#{Regexp.escape(netrc_content)}$/).any?
end
- def run_git(command_str, env: self.env_vars, max_attempts: 1)
+ def run_git(command_str, env: env_vars, max_attempts: 1)
run(command_str, env: env, max_attempts: max_attempts, log_prefix: 'Git: ')
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 83db8bc0fd6..248c5f38438 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -218,6 +218,15 @@ module QA
page.validate_elements_present! if page
end
+ # Uses capybara to locate and click an element instead of `click_element`.
+ # This can be used when it's not possible to add a QA selector but we still want to log the click
+ #
+ # @param [String] method the capybara method to use
+ # @param [String] locator the selector used to find the element
+ def click_via_capybara(method, locator)
+ page.public_send(method, locator)
+ end
+
def fill_element(name, content)
find_element(name).set(content)
end
@@ -452,7 +461,7 @@ module QA
click_by_js ? page.execute_script("arguments[0].click();", box) : box.click
end
- def feature_flag_controlled_element(feature_flag, element_when_flag_enabled, element_when_flag_disabled)
+ def feature_flag_controlled_element(feature_flag, element_when_flag_enabled, element_when_flag_disabled, visibility = true)
# Feature flags can change the UI elements shown, but we need admin access to get feature flag values, which
# prevents us running the tests on production. Instead we detect the UI element that should be shown when the
# feature flag is enabled and otherwise use the element that should be displayed when the feature flag is
@@ -463,12 +472,12 @@ module QA
# load and render the UI
wait_for_requests
- return element_when_flag_enabled if has_element?(element_when_flag_enabled, wait: 1)
- return element_when_flag_disabled if has_element?(element_when_flag_disabled, wait: 1)
+ return element_when_flag_enabled if has_element?(element_when_flag_enabled, wait: 1, visible: visibility)
+ return element_when_flag_disabled if has_element?(element_when_flag_disabled, wait: 1, visibile: visibility)
# Check both options again, this time waiting for the default duration
- return element_when_flag_enabled if has_element?(element_when_flag_enabled)
- return element_when_flag_disabled if has_element?(element_when_flag_disabled)
+ return element_when_flag_enabled if has_element?(element_when_flag_enabled, visible: visibility)
+ return element_when_flag_disabled if has_element?(element_when_flag_disabled, visible: visibility)
raise ElementNotFound,
"Could not find the expected element as #{element_when_flag_enabled} or #{element_when_flag_disabled}." \
diff --git a/qa/qa/page/component/blob_content.rb b/qa/qa/page/component/blob_content.rb
index ce743b24dda..c2a1687ccfc 100644
--- a/qa/qa/page/component/blob_content.rb
+++ b/qa/qa/page/component/blob_content.rb
@@ -63,6 +63,17 @@ module QA
end
end
+ def has_normalized_ws_text?(text, wait: Capybara.default_max_wait_time)
+ if has_element?(:blob_viewer_file_content, wait: 1)
+ # The blob viewer renders line numbers and whitespace in a way that doesn't match the source file
+ # This isn't a visual validation test, so we ignore line numbers and whitespace
+ find_element(:blob_viewer_file_content, wait: 0).text.gsub(/^\d+\s|\s*/, '')
+ .start_with?(text.gsub(/\s*/, ''))
+ else
+ has_text?(text.gsub(/\s+/, " "), wait: wait)
+ end
+ end
+
def click_copy_file_contents(file_number = nil)
within_file_by_number(:default_actions_container, file_number) { click_element(:copy_contents_button) }
end
diff --git a/qa/qa/page/component/new_snippet.rb b/qa/qa/page/component/new_snippet.rb
index 6ccf8a4043e..9c4408e36e4 100644
--- a/qa/qa/page/component/new_snippet.rb
+++ b/qa/qa/page/component/new_snippet.rb
@@ -76,8 +76,7 @@ module QA
end
def click_create_snippet_button
- wait_until(reload: false) { !find_element(:submit_button).disabled? }
- click_element(:submit_button)
+ click_element_coordinates(:submit_button)
wait_until(reload: false) do
has_no_element?(:snippet_title_field)
end
diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb
index a8ae706858e..47d32ae8225 100644
--- a/qa/qa/page/component/snippet.rb
+++ b/qa/qa/page/component/snippet.rb
@@ -82,7 +82,7 @@ module QA
base.view 'app/views/layouts/nav/_breadcrumbs.html.haml' do
element :breadcrumb_links_content
- element :breadcrumb_sub_title_content
+ element :breadcrumb_current_link
end
end
@@ -257,7 +257,7 @@ module QA
def snippet_id
within_element(:breadcrumb_links_content) do
- find_element(:breadcrumb_sub_title_content).text.delete_prefix('$')
+ find_element(:breadcrumb_current_link).text.delete_prefix('$')
end
end
end
diff --git a/qa/qa/page/dashboard/snippet/edit.rb b/qa/qa/page/dashboard/snippet/edit.rb
index d84a053591c..8af3eb5693c 100644
--- a/qa/qa/page/dashboard/snippet/edit.rb
+++ b/qa/qa/page/dashboard/snippet/edit.rb
@@ -63,8 +63,7 @@ module QA
end
def save_changes
- wait_until(reload: false) { !find_element(:submit_button).disabled? }
- click_element(:submit_button)
+ click_element_coordinates(:submit_button)
wait_until(reload: false) do
has_no_element?(:file_name_field)
end
diff --git a/qa/qa/page/file/form.rb b/qa/qa/page/file/form.rb
index a6251f185f9..bb8934db498 100644
--- a/qa/qa/page/file/form.rb
+++ b/qa/qa/page/file/form.rb
@@ -4,8 +4,9 @@ module QA
module Page
module File
class Form < Page::Base
- include Shared::CommitMessage
include Page::Component::DropdownFilter
+ include Page::Component::BlobContent
+ include Shared::CommitMessage
include Shared::CommitButton
include Shared::Editor
diff --git a/qa/qa/page/group/settings/group_deploy_tokens.rb b/qa/qa/page/group/settings/group_deploy_tokens.rb
index 65ee3fc72eb..7d908f473de 100644
--- a/qa/qa/page/group/settings/group_deploy_tokens.rb
+++ b/qa/qa/page/group/settings/group_deploy_tokens.rb
@@ -30,10 +30,10 @@ module QA
end
def fill_scopes(read_repository: false, read_registry: false, read_package_registry: false, write_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
- check_element(:deploy_token_write_package_registry_checkbox) if write_package_registry
+ check_element(:deploy_token_read_repository_checkbox, true) if read_repository
+ check_element(:deploy_token_read_package_registry_checkbox, true) if read_package_registry
+ check_element(:deploy_token_read_registry_checkbox, true) if read_registry
+ check_element(:deploy_token_write_package_registry_checkbox, true) if write_package_registry
end
def add_token
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index d8b7bb90437..f3ee627c41e 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -30,9 +30,8 @@ module QA
element :register_tab
end
- view 'app/views/devise/shared/_tabs_normal.html.haml' do
+ view 'app/views/devise/shared/_tab_single.html.haml' do
element :sign_in_tab
- element :register_tab
end
view 'app/helpers/auth_helper.rb' do
@@ -134,6 +133,14 @@ module QA
has_css?('[name="arkose_labs_token"][value]', visible: false)
end
+ def has_accept_all_cookies_button?
+ has_button?('Accept All Cookies')
+ end
+
+ def click_accept_all_cookies
+ click_button('Accept All Cookies')
+ end
+
def switch_to_sign_in_tab
click_element :sign_in_tab
end
@@ -180,6 +187,7 @@ module QA
fill_element :password_field, user.password
if Runtime::Env.running_on_dot_com?
+ click_accept_all_cookies if has_accept_all_cookies_button?
# Arkose only appears in staging.gitlab.com, gitlab.com, etc...
# Wait until the ArkoseLabs challenge has initialized
diff --git a/qa/qa/page/merge_request/index.rb b/qa/qa/page/merge_request/index.rb
new file mode 100644
index 00000000000..ae024c16566
--- /dev/null
+++ b/qa/qa/page/merge_request/index.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module MergeRequest
+ class Index < Page::Base
+ view 'app/views/shared/empty_states/_merge_requests.html.haml' do
+ element :new_merge_request_button
+ end
+
+ def click_new_merge_request
+ click_element(:new_merge_request_button)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index bcc60a8275d..b19a0d1a830 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -12,6 +12,11 @@ module QA
element :issuable_form_description
end
+ view 'app/views/projects/merge_requests/creations/_new_compare.html.haml' do
+ element :compare_branches_button
+ element :source_branch_dropdown
+ end
+
view 'app/views/projects/merge_requests/show.html.haml' do
element :diffs_tab
end
@@ -27,6 +32,10 @@ module QA
"to customize #{scanner_name} settings."
end
+ def click_compare_branches_and_continue
+ click_element(:compare_branches_button)
+ end
+
def create_merge_request
click_element(:issuable_create_button, Page::MergeRequest::Show)
end
@@ -43,6 +52,12 @@ module QA
def has_file?(file_name)
has_element?(:file_name_content, text: file_name)
end
+
+ def select_source_branch(branch)
+ click_element(:source_branch_dropdown)
+ fill_element(:dropdown_input_field, branch)
+ click_via_capybara(:click_on, branch)
+ end
end
end
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 689b3dba286..8f5ac62d127 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -49,8 +49,8 @@ module QA
element :comment_now_button
end
- view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue' do
- element :download_dropdown
+ view 'app/views/projects/merge_requests/_code_dropdown.html.haml' do
+ element :mr_code_dropdown
element :download_email_patches_menu_item
element :download_plain_diff_menu_item
element :open_in_web_ide_button
@@ -343,12 +343,12 @@ module QA
end
def view_email_patches
- click_element(:download_dropdown)
+ click_element(:mr_code_dropdown)
visit_link_in_element(:download_email_patches_menu_item)
end
def view_plain_diff
- click_element(:download_dropdown)
+ click_element(:mr_code_dropdown)
visit_link_in_element(:download_plain_diff_menu_item)
end
@@ -359,6 +359,7 @@ module QA
end
def click_open_in_web_ide
+ click_element(:mr_code_dropdown)
click_element(:open_in_web_ide_button)
wait_for_requests
end
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index 96a48e6240a..6cf5c3b1134 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -16,9 +16,9 @@ module QA
click_element(:run_pipeline_button, Page::Project::Pipeline::Show)
end
- def add_variable(key, value, row_index: 0)
+ def configure_variable(key: nil, value: 'foo', row_index: 0)
within_element_by_index(:ci_variable_row_container, row_index) do
- fill_element(:ci_variable_key_field, key)
+ fill_element(:ci_variable_key_field, key) unless key.nil?
fill_element(:ci_variable_value_field, value)
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index f499b748fb4..3f1cc2f1257 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -74,7 +74,11 @@ module QA
end
def has_linked_pipeline?(title: nil)
- title ? find_linked_pipeline_by_title(title) : has_element?(:linked_pipeline_container)
+ # If the pipeline page has loaded linked pipelines should appear, but it can take a little while,
+ # especially on busier environments.
+ retry_until(reload: true, message: 'Waiting for linked pipeline to appear') do
+ title ? find_linked_pipeline_by_title(title) : has_element?(:linked_pipeline_container)
+ end
end
alias_method :has_child_pipeline?, :has_linked_pipeline?
diff --git a/qa/qa/page/project/pipeline_editor/show.rb b/qa/qa/page/project/pipeline_editor/show.rb
index 1a8e1e07994..197fd8fd9fb 100644
--- a/qa/qa/page/project/pipeline_editor/show.rb
+++ b/qa/qa/page/project/pipeline_editor/show.rb
@@ -5,6 +5,10 @@ module QA
module Project
module PipelineEditor
class Show < QA::Page::Base
+ view 'app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue' do
+ element :pipeline_editor_app, required: true
+ end
+
view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
element :branch_selector_button, required: true
element :branch_menu_item_button
@@ -12,7 +16,7 @@ module QA
end
view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
- element :target_branch_field, required: true
+ element :source_branch_field, required: true
end
view 'app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue' do
@@ -46,7 +50,23 @@ module QA
element :file_editor_container
end
+ view 'app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue' do
+ element :file_tree_popover
+ end
+
+ def initialize
+ dismiss_file_tree_popover if has_element?(:file_tree_popover)
+
+ super
+ end
+
+ def dismiss_file_tree_popover
+ # clicking outside the popover will dismiss it
+ click_element(:pipeline_editor_app)
+ end
+
def open_branch_selector_dropdown
+ dismiss_file_tree_popover if has_element?(:file_tree_popover, wait: 1)
click_element(:branch_selector_button)
end
@@ -57,8 +77,8 @@ module QA
wait_for_requests
end
- def target_branch_name
- find_element(:target_branch_field).value
+ def source_branch_name
+ find_element(:source_branch_field).value
end
def editing_content
@@ -76,8 +96,8 @@ module QA
wait_for_requests
end
- def set_target_branch(name)
- find_element(:target_branch_field).fill_in(with: name)
+ def set_source_branch(name)
+ find_element(:source_branch_field).fill_in(with: name)
end
def current_branch
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
index 6df285cdd93..c5fad2efcfe 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -11,13 +11,6 @@ module QA
element :autodevops_settings_content
element :runners_settings_content
element :variables_settings_content
- element :general_pipelines_settings_content
- end
-
- def expand_general_pipelines(&block)
- expand_content(:general_pipelines_settings_content) do
- Settings::GeneralPipelines.perform(&block)
- end
end
def expand_runners_settings(&block)
diff --git a/qa/qa/page/project/settings/deploy_tokens.rb b/qa/qa/page/project/settings/deploy_tokens.rb
index 407d57bc54e..cf25f4a0568 100644
--- a/qa/qa/page/project/settings/deploy_tokens.rb
+++ b/qa/qa/page/project/settings/deploy_tokens.rb
@@ -31,11 +31,25 @@ module QA
end
def fill_scopes(scopes)
- check_element(:deploy_token_read_repository_checkbox) if scopes.include? :read_repository
- check_element(:deploy_token_read_package_registry_checkbox) if scopes.include? :read_package_registry
- check_element(:deploy_token_write_package_registry_checkbox) if scopes.include? :write_package_registry
- check_element(:deploy_token_read_registry_checkbox) if scopes.include? :read_registry
- check_element(:deploy_token_write_registry_checkbox) if scopes.include? :write_registry
+ if scopes.include? :read_repository
+ check_element(:deploy_token_read_repository_checkbox, true)
+ end
+
+ if scopes.include? :read_package_registry
+ check_element(:deploy_token_read_package_registry_checkbox, true)
+ end
+
+ if scopes.include? :write_package_registry
+ check_element(:deploy_token_write_package_registry_checkbox, true)
+ end
+
+ if scopes.include? :read_registry
+ check_element(:deploy_token_read_registry_checkbox, true)
+ end
+
+ if scopes.include? :write_registry
+ check_element(:deploy_token_write_registry_checkbox, true)
+ end
end
def add_token
diff --git a/qa/qa/page/project/settings/general_pipelines.rb b/qa/qa/page/project/settings/general_pipelines.rb
deleted file mode 100644
index 5a98849a41d..00000000000
--- a/qa/qa/page/project/settings/general_pipelines.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Settings
- class GeneralPipelines < Page::Base
- include QA::Page::Settings::Common
-
- view 'app/views/projects/settings/ci_cd/_form.html.haml' do
- element :build_coverage_regex_field
- element :save_general_pipelines_changes_button
- end
-
- def configure_coverage_regex(pattern)
- fill_element :build_coverage_regex_field, pattern
- click_element :save_general_pipelines_changes_button
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index dbe804bfdd0..0d5d4df9f34 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -29,7 +29,7 @@ module QA
end
def enable_merge_if_all_disscussions_are_resolved
- check_element(:allow_merge_if_all_discussions_are_resolved_checkbox)
+ check_element(:allow_merge_if_all_discussions_are_resolved_checkbox, true)
click_save_changes
end
end
diff --git a/qa/qa/page/project/settings/services/prometheus.rb b/qa/qa/page/project/settings/services/prometheus.rb
index 8ae4ded535e..2e3c385a27d 100644
--- a/qa/qa/page/project/settings/services/prometheus.rb
+++ b/qa/qa/page/project/settings/services/prometheus.rb
@@ -8,7 +8,7 @@ module QA
class Prometheus < Page::Base
include Page::Component::CustomMetric
- view 'app/views/projects/services/prometheus/_custom_metrics.html.haml' do
+ view 'app/views/shared/integrations/prometheus/_custom_metrics.html.haml' do
element :custom_metrics_container
element :new_metric_button
end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index b234a9ba986..9983ee2a33d 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -120,6 +120,10 @@ module QA
click_element(:new_issue_link)
end
+ def has_create_merge_request_button?
+ has_css?(element_selector_css(:create_merge_request))
+ end
+
def has_file?(name)
return false unless has_element?(:file_tree_table)
@@ -144,7 +148,7 @@ module QA
def new_merge_request
wait_until(reload: true) do
- has_css?(element_selector_css(:create_merge_request))
+ has_create_merge_request_button?
end
click_element :create_merge_request
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index 889197a0373..bda72703906 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -64,10 +64,6 @@ module QA
end
end
- def marked_for_deletion?
- reload!.api_response[:marked_for_deletion_on].present?
- end
-
# Get group badges
#
# @return [Array<QA::Resource::GroupBadge>]
diff --git a/qa/qa/resource/group_deploy_token.rb b/qa/qa/resource/group_deploy_token.rb
index c1d6be6547a..4c9b296ece1 100644
--- a/qa/qa/resource/group_deploy_token.rb
+++ b/qa/qa/resource/group_deploy_token.rb
@@ -17,10 +17,6 @@ module QA
end
end
- def fabricate_via_api!
- super
- end
-
def api_get_path
"/groups/#{group.id}/deploy_tokens"
end
diff --git a/qa/qa/resource/job.rb b/qa/qa/resource/job.rb
new file mode 100644
index 00000000000..96c502e567c
--- /dev/null
+++ b/qa/qa/resource/job.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Job < Base
+ attr_accessor :id, :name, :project
+
+ attributes :id,
+ :project
+
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def artifacts
+ parse_body(api_get_from(api_get_path))[:artifacts]
+ end
+
+ def api_get_path
+ "/projects/#{project.id}/jobs/#{id}"
+ end
+
+ def api_post_path
+ end
+
+ def api_post_body
+ {
+ artifacts: artifacts
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/members.rb b/qa/qa/resource/members.rb
index 83adb10c3a0..0061f74cec5 100644
--- a/qa/qa/resource/members.rb
+++ b/qa/qa/resource/members.rb
@@ -12,7 +12,8 @@ module QA
QA::Runtime::Logger.debug(%Q[Adding user #{user.username} to #{full_path} #{self.class.name}])
response = post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
- response.code == QA::Support::API::HTTP_STATUS_CREATED
+ break true if response.code == QA::Support::API::HTTP_STATUS_CREATED
+ break true if response.body.include?('Member already exists')
end
end
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index 685cccea02d..0a92553690f 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -78,12 +78,12 @@ module QA
end
def fabricate!
- return fabricate_large_merge_request if Runtime::Scenario.large_setup?
+ return fabricate_large_merge_request if large_setup?
populate_target_and_source_if_required
project.visit!
- Page::Project::Show.perform(&:new_merge_request)
+ Flow::MergeRequest.create_new(source_branch: source_branch)
Page::MergeRequest::New.perform do |new_page|
new_page.fill_title(@title)
new_page.choose_template(@template) if @template
@@ -100,7 +100,7 @@ module QA
end
def fabricate_via_api!
- return fabricate_large_merge_request if Runtime::Scenario.large_setup?
+ return fabricate_large_merge_request if large_setup?
resource_web_url(api_get)
rescue ResourceNotFoundError, NoValueError # rescue if iid not populated
@@ -208,6 +208,12 @@ module QA
private
+ def large_setup?
+ Runtime::Scenario.large_setup?
+ rescue ArgumentError
+ false
+ end
+
def transform_api_resource(api_resource)
raise ResourceNotFoundError if api_resource.blank?
diff --git a/qa/qa/resource/pipeline.rb b/qa/qa/resource/pipeline.rb
index 907a6cb8558..910065d76a8 100644
--- a/qa/qa/resource/pipeline.rb
+++ b/qa/qa/resource/pipeline.rb
@@ -9,10 +9,10 @@ module QA
end
end
- attribute :id
- attribute :status
- attribute :ref
- attribute :sha
+ attributes :id,
+ :status,
+ :ref,
+ :sha
# array in form
# [
@@ -33,6 +33,14 @@ module QA
Page::Project::Pipeline::New.perform(&:click_run_pipeline_button)
end
+ def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
+ super
+ rescue NoValueError
+ super
+ end
+
def ref
project.default_branch
end
@@ -51,6 +59,40 @@ module QA
variables: variables
}
end
+
+ def pipeline_variables
+ response = get(request_url("#{api_get_path}/variables"))
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceQueryError, "Could not get variables. Request returned (#{response.code}): `#{response}`."
+ end
+
+ parse_body(response)
+ end
+
+ def has_variable?(key:, value:)
+ pipeline_variables.any? { |var| var[:key] == key && var[:value] == value }
+ end
+
+ def has_no_variable?(key:, value:)
+ !pipeline_variables.any? { |var| var[:key] == key && var[:value] == value }
+ end
+
+ def pipeline_bridges
+ response = get(request_url("#{api_get_path}/bridges"))
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceQueryError, "Could not get bridges. Request returned (#{response.code}): `#{response}`."
+ end
+
+ parse_body(response)
+ end
+
+ def downstream_pipeline_id(bridge_name:)
+ result = pipeline_bridges.find { |bridge| bridge[:name] == bridge_name }
+
+ result[:downstream_pipeline][:id]
+ end
end
end
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index dba3eb2e219..2db4f4b5f65 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -190,6 +190,10 @@ module QA
"#{api_get_path}/pipeline_schedules"
end
+ def api_jobs_path
+ "#{api_get_path}/jobs"
+ end
+
def api_issues_path
"#{api_get_path}/issues"
end
@@ -206,6 +210,10 @@ module QA
"#{api_get_path}/wikis"
end
+ def api_releases_path
+ "#{api_get_path}/releases"
+ end
+
def api_post_body
post_body = {
name: name,
@@ -354,6 +362,15 @@ module QA
auto_paginated_response(request_url(api_pipelines_path, per_page: '100'), attempts: attempts)
end
+ def jobs
+ response = get(request_url(api_jobs_path))
+ parse_body(response)
+ end
+
+ def job_by_name(job_name)
+ jobs.find { |job| job[:name] == job_name }
+ end
+
def issues(auto_paginate: false, attempts: 0)
return parse_body(api_get_from(api_issues_path)) unless auto_paginate
@@ -381,9 +398,22 @@ module QA
api_post_to(api_wikis_path, title: title, content: content)
end
+ def releases
+ response = api_get_from(api_releases_path)
+ parse_body(response)
+ end
+
+ def create_release(tag, ref = default_branch, **params)
+ api_post_to(api_releases_path, tag_name: tag, ref: ref, **params)
+ end
+
# Uses the API to wait until a pull mirroring update is successful (pull mirroring is treated as an import)
def wait_for_pull_mirroring
- mirror_succeeded = Support::Retrier.retry_until(max_duration: 180, raise_on_failure: false, sleep_interval: 1) do
+ mirror_succeeded = Support::Retrier.retry_until(
+ max_duration: 180,
+ raise_on_failure: false,
+ sleep_interval: 1
+ ) do
reload!
api_resource[:import_status] == "finished"
end
@@ -394,7 +424,11 @@ module QA
def remove_via_api!
super
- Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1, message: "Waiting for #{self.class.name} to be removed") do
+ Support::Retrier.retry_until(
+ max_duration: 60,
+ sleep_interval: 1,
+ message: "Waiting for #{self.class.name} to be removed"
+ ) do
!exists?
rescue InternalServerError
# Retry on transient errors that are likely to be due to race conditions between concurrent delete operations
diff --git a/qa/qa/resource/project_access_token.rb b/qa/qa/resource/project_access_token.rb
index f5cd8798f19..58cb3e667c0 100644
--- a/qa/qa/resource/project_access_token.rb
+++ b/qa/qa/resource/project_access_token.rb
@@ -15,10 +15,6 @@ module QA
Page::Project::Settings::AccessTokens.perform(&:created_access_token)
end
- def fabricate_via_api!
- super
- end
-
def api_get_path
"/projects/#{project.api_resource[:id]}/access_tokens"
end
diff --git a/qa/qa/resource/project_deploy_token.rb b/qa/qa/resource/project_deploy_token.rb
index b31a7c25157..d6125103025 100644
--- a/qa/qa/resource/project_deploy_token.rb
+++ b/qa/qa/resource/project_deploy_token.rb
@@ -17,10 +17,6 @@ module QA
end
end
- def fabricate_via_api!
- super
- end
-
def api_get_path
"/projects/#{project.id}/deploy_tokens"
end
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
index 28a0f12b3e3..b9dbd2a6131 100644
--- a/qa/qa/resource/project_imported_from_github.rb
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -17,7 +17,7 @@ module QA
Page::Project::Import::Github.perform do |import_page|
import_page.add_personal_access_token(github_personal_access_token)
import_page.import!(github_repository_path, group.full_path, name)
- import_page.wait_for_success(github_repository_path)
+ import_page.wait_for_success(github_repository_path, wait: 240)
end
reload!
diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb
index 54093a5c195..1fe35f7a77d 100644
--- a/qa/qa/resource/repository/commit.rb
+++ b/qa/qa/resource/repository/commit.rb
@@ -33,7 +33,11 @@ module QA
super
rescue ResourceNotFoundError
- super
+ result = super
+
+ project.wait_for_push(commit_message)
+
+ result
end
def api_get_path
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 89e84f414b1..03178661826 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -18,10 +18,6 @@ module QA
CAPYBARA_MAX_WAIT_TIME = 10
- def initialize
- self.class.configure!
- end
-
def self.blank_page?
['', 'about:blank', 'data:,'].include?(Capybara.current_session.driver.browser.current_url)
rescue StandardError
@@ -49,6 +45,8 @@ module QA
# rubocop: disable Metrics/AbcSize
def self.configure!
+ return if @configured
+
RSpec.configure do |config|
config.define_derived_metadata(file_path: %r{/qa/specs/features/}) do |metadata|
metadata[:type] = :feature
@@ -57,23 +55,19 @@ module QA
config.append_after(:each) do |example|
if example.metadata[:screenshot]
screenshot = example.metadata[:screenshot][:image] || example.metadata[:screenshot][:html]
- example.metadata[:stdout] = %{[[ATTACHMENT|#{screenshot}]]}
+ example.metadata[:stdout] = %([[ATTACHMENT|#{screenshot}]])
end
end
end
Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i
- 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)
case QA::Runtime::Env.browser
when :chrome
- if QA::Runtime::Env.accept_insecure_certs?
- capabilities['acceptInsecureCerts'] = true
- end
+ capabilities['acceptInsecureCerts'] = true if QA::Runtime::Env.accept_insecure_certs?
# set logging preferences
# this enables access to logs with `page.driver.manage.get_log(:browser)`
@@ -103,7 +97,9 @@ module QA
# Specify the user-agent to allow challenges to be bypassed
# See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
- capabilities['goog:chromeOptions'][:args] << "user-agent=#{QA::Runtime::Env.user_agent}" if QA::Runtime::Env.user_agent
+ if QA::Runtime::Env.user_agent
+ capabilities['goog:chromeOptions'][:args] << "user-agent=#{QA::Runtime::Env.user_agent}"
+ end
if QA::Runtime::Env.remote_mobile_device_name
capabilities['platformName'] = 'Android'
@@ -121,9 +117,7 @@ module QA
end
when :firefox
- if QA::Runtime::Env.accept_insecure_certs?
- capabilities['acceptInsecureCerts'] = true
- end
+ capabilities['acceptInsecureCerts'] = true if QA::Runtime::Env.accept_insecure_certs?
end
# Use the same profile on QA runs if CHROME_REUSE_PROFILE is true.
@@ -162,7 +156,10 @@ module QA
Capybara::Screenshot.append_timestamp = false
Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example|
- ::File.join(QA::Runtime::Namespace.name(reset_cache: false), example.full_description.downcase.parameterize(separator: "_")[0..99])
+ ::File.join(
+ QA::Runtime::Namespace.name(reset_cache: false),
+ example.full_description.downcase.parameterize(separator: "_")[0..99]
+ )
end
Capybara.configure do |config|
@@ -183,6 +180,8 @@ module QA
config.base_url = Runtime::Scenario.attributes[:gitlab_address] # reuse GitLab address
config.hide_banner = true
end
+
+ @configured = true
end
# rubocop: enable Metrics/AbcSize
@@ -221,7 +220,7 @@ module QA
end
end
- yield.tap { clear! } if block_given?
+ yield.tap { clear! } if block
end
# To redirect the browser to a canary or non-canary web node
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index e4537009406..0db5314de4d 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -25,6 +25,10 @@ module QA
SUPPORTED_FEATURES
end
+ def gitlab_url
+ @gitlab_url ||= ENV["QA_GITLAB_URL"] || "http://127.0.0.1:3000" # default to GDK
+ end
+
def additional_repository_storage
ENV['QA_ADDITIONAL_REPOSITORY_STORAGE']
end
@@ -38,7 +42,7 @@ module QA
end
def interception_enabled?
- enabled?(ENV['INTERCEPT_REQUESTS'], default: true)
+ enabled?(ENV['QA_INTERCEPT_REQUESTS'], default: false)
end
def can_intercept?
diff --git a/qa/qa/runtime/feature.rb b/qa/qa/runtime/feature.rb
index ec28813c1f6..93e215e9635 100644
--- a/qa/qa/runtime/feature.rb
+++ b/qa/qa/runtime/feature.rb
@@ -42,6 +42,8 @@ module QA
enable(flag, **scopes)
when 'disabled', 'disable', 'false', 0, false
disable(flag, **scopes)
+ when 'deleted'
+ QA::Runtime::Logger.info("Feature flag definition for '#{flag}' was deleted. The state of the feature flag has not been changed.")
else
raise UnknownStateError, "Unknown feature flag state: #{state}"
end
diff --git a/qa/qa/runtime/ip_address.rb b/qa/qa/runtime/ip_address.rb
index bec5c412a6a..657dc789cff 100644
--- a/qa/qa/runtime/ip_address.rb
+++ b/qa/qa/runtime/ip_address.rb
@@ -13,7 +13,7 @@ module QA
def fetch_current_ip_address
# When running on CI against a live environment such as staging.gitlab.com,
# we use the public facing IP address
- ip_address = if Env.running_in_ci? && !URI.parse(Scenario.gitlab_address).host.include?('test')
+ ip_address = if Env.running_in_ci? && !URI.parse(Scenario.gitlab_address).host.include?('.test')
response = get(PUBLIC_IP_ADDRESS_API)
raise HostUnreachableError, "#{PUBLIC_IP_ADDRESS_API} is unreachable" unless response.code == Support::API::HTTP_STATUS_OK
diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb
index 8cf1fa0705f..99906ca44d9 100644
--- a/qa/qa/scenario/template.rb
+++ b/qa/qa/scenario/template.rb
@@ -21,33 +21,30 @@ module QA
end
def perform(options, *args)
- extract_address(:gitlab_address, options, args)
-
- gitlab_address = URI(Runtime::Scenario.gitlab_address)
-
- # Define the "About" page as an `about` subdomain.
- # @example
- # Given *gitlab_address* = 'https://gitlab.com/' #=> https://about.gitlab.com/
- # Given *gitlab_address* = 'https://staging.gitlab.com/' #=> https://about.staging.gitlab.com/
- # Given *gitlab_address* = 'http://gitlab-abc123.test/' #=> http://about.gitlab-abc123.test/
- Runtime::Scenario.define(:about_address, URI(-> { gitlab_address.host = "about.#{gitlab_address.host}"; gitlab_address }.call).to_s) # rubocop:disable Style/Semicolon
+ define_gitlab_address(options, args)
# Save the scenario class name
Runtime::Scenario.define(:klass, self.class.name)
+ # Set large setup attribute
+ Runtime::Scenario.define(:large_setup?, args.include?('can_use_large_setup'))
+
##
- # Setup knapsack and download latest report
+ # Configure browser
#
- Tools::KnapsackReport.configure! if Runtime::Env.knapsack?
+ Runtime::Browser.configure!
##
# Perform before hooks, which are different for CE and EE
#
-
- Runtime::Release.perform_before_hooks unless Runtime::Env.dry_run
+ QA::Runtime::Release.perform_before_hooks unless QA::Runtime::Env.dry_run
Runtime::Feature.enable(options[:enable_feature]) if options.key?(:enable_feature)
- Runtime::Feature.disable(options[:disable_feature]) if options.key?(:disable_feature) && (@feature_enabled = Runtime::Feature.enabled?(options[:disable_feature]))
+
+ if options.key?(:disable_feature) && (@feature_enabled = Runtime::Feature.enabled?(options[:disable_feature]))
+ Runtime::Feature.disable(options[:disable_feature])
+ end
+
Runtime::Feature.set(options[:set_feature_flags]) if options.key?(:set_feature_flags)
Specs::Runner.perform do |specs|
@@ -60,25 +57,31 @@ module QA
Runtime::Feature.enable(options[:disable_feature]) if options.key?(:disable_feature) && @feature_enabled
end
- def extract_option(name, options, args)
- option = if options.key?(name)
- options[name]
- else
- args.shift
- end
-
- Runtime::Scenario.define(name, option)
+ def extract_address(name, options)
+ address = options[name]
+ validate_address(name, address)
- option
+ Runtime::Scenario.define(name, address)
end
- # For backwards-compatibility, if the gitlab instance address is not
- # specified as an option parsed by OptionParser, it can be specified as
- # the first argument
- def extract_address(name, options, args)
- address = extract_option(name, options, args)
+ private
+
+ delegate :define_gitlab_address_attribute!, to: 'QA::Support::GitlabAddress'
+
+ # Define gitlab address attribute
+ #
+ # Use first argument if a valid address, else use named argument or default to environment variable
+ #
+ # @param [Hash] options
+ # @param [Array] args
+ # @return [void]
+ def define_gitlab_address(options, args)
+ address_from_opt = Runtime::Scenario.attributes[:gitlab_address]
+
+ return define_gitlab_address_attribute!(args.shift) if args.first && Runtime::Address.valid?(args.first)
+ return define_gitlab_address_attribute!(address_from_opt) if address_from_opt
- raise ::ArgumentError, "The address provided for `#{name}` is not valid: #{address}" unless Runtime::Address.valid?(address)
+ define_gitlab_address_attribute!
end
end
end
diff --git a/qa/qa/scenario/test/integration/mattermost.rb b/qa/qa/scenario/test/integration/mattermost.rb
index 48ebb51966c..145c767a38c 100644
--- a/qa/qa/scenario/test/integration/mattermost.rb
+++ b/qa/qa/scenario/test/integration/mattermost.rb
@@ -12,13 +12,6 @@ module QA
tags :mattermost
attribute :mattermost_address, '--mattermost-address URL', 'Address of the Mattermost server'
-
- def perform(options, *args)
- extract_address(:gitlab_address, options, args)
- extract_address(:mattermost_address, options, args)
-
- super(options, *args)
- end
end
end
end
diff --git a/qa/qa/service/cluster_provider/k3s_cilium.rb b/qa/qa/service/cluster_provider/k3s_cilium.rb
deleted file mode 100644
index 5b529caa20b..00000000000
--- a/qa/qa/service/cluster_provider/k3s_cilium.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-# 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/specs/features/api/1_manage/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_github_repo_spec.rb
index 79bba484bea..79509bdbe01 100644
--- 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
@@ -36,7 +36,7 @@ module QA
imported_project.reload! # import the project
expect { imported_project.project_import_status[:import_status] }.to eventually_eq('finished')
- .within(max_duration: 90, sleep_interval: 1)
+ .within(max_duration: 240, sleep_interval: 1)
aggregate_failures do
verify_status_data
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
new file mode 100644
index 00000000000..201b8efdf6a
--- /dev/null
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require_relative 'gitlab_project_migration_common'
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Gitlab migration' do
+ include_context 'with gitlab project migration'
+
+ context 'with release' do
+ let(:tag) { 'v0.0.1' }
+ let(:source_project_with_readme) { true }
+
+ let(:milestone) do
+ Resource::ProjectMilestone.fabricate_via_api! do |resource|
+ resource.project = source_project
+ resource.api_client = api_client
+ end
+ end
+
+ let(:source_release) { comparable_release(source_project.releases.find { |r| r[:tag_name] == tag }) }
+ let(:imported_release) { comparable_release(imported_releases.find { |r| r[:tag_name] == tag }) }
+ let(:imported_releases) { imported_project.releases }
+
+ # Update release object to be comparable
+ #
+ # Convert objects with project specific attributes like paths and urls to be comparable
+ #
+ # @param [Hash] release
+ # @return [Hash]
+ def comparable_release(release)
+ release&.except(:_links, :evidences)&.merge(
+ {
+ author: release[:author].except(:web_url),
+ commit: release[:commit].except(:web_url),
+ commit_path: release[:commit_path].split("/-/").last,
+ tag_path: release[:tag_path].split("/-/").last,
+ assets: release[:assets].merge({
+ sources: release.dig(:assets, :sources).map do |source|
+ source.merge({ url: source[:url].split("/-/").last })
+ end
+ }),
+ milestones: release[:milestones].map do |milestone|
+ milestone.except(:id, :project_id).merge({ web_url: milestone[:web_url].split("/-/").last })
+ end
+ # TODO: Add back evidence testing once implemented
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/360567
+ # evidences: release[:evidences].map do |evidence|
+ # evidence.merge({ filepath: evidence[:filepath].split("/-/").last })
+ # end
+ }
+ )
+ end
+
+ before do
+ source_project.create_release(tag, milestones: [milestone.title])
+ end
+
+ it(
+ 'successfully imports project release',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360243'
+ ) do
+ expect_import_finished
+
+ expect(imported_releases.size).to eq(1), "Expected to have 1 migrated release"
+ expect(imported_release).to eq(source_release)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb
index 6480b880400..33ec24d1be1 100644
--- a/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/project_access_token_spec.rb
@@ -11,7 +11,7 @@ module QA
@user_api_client = Runtime::API::Client.new(:gitlab, personal_access_token: @project_access_token.token)
end
- context 'for the same project' do
+ context 'for the same project', :reliable do
it 'can be used to create a file via the project API', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347858' do
expect do
Resource::File.fabricate_via_api! do |file|
diff --git a/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb b/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb
index fe6c89f4ee4..e518bbfc6f7 100644
--- a/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/user_access_termination_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'User', :requires_admin do
+ describe 'User', :requires_admin, :reliable do
before(:all) do
admin_api_client = Runtime::API::Client.as_admin
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 d066953d12e..b6296b5a263 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
@@ -3,7 +3,10 @@
require 'parallel'
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361382',
+ type: :investigating
+ } do
context 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
let(:praefect_manager) { Service::PraefectManager.new }
let(:project) do
diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
index 4d28937fbf8..151fd0fffe3 100644
--- a/qa/qa/specs/features/api/3_create/repository/files_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
@@ -3,68 +3,69 @@
require 'airborne'
module QA
- RSpec.describe 'API basics' do
- before(:context) do
- @api_client = Runtime::API::Client.new(:gitlab)
- end
+ RSpec.describe 'Create' do
+ describe 'API basics' do
+ before(:context) do
+ @api_client = Runtime::API::Client.new(:gitlab)
+ end
- let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
- let(:sanitized_project_path) { CGI.escape("#{Runtime::User.username}/#{project_name}") }
+ let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
+ let(:sanitized_project_path) { CGI.escape("#{Runtime::User.username}/#{project_name}") }
- it 'user creates a project with a file and deletes them afterwards', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347745' do
- create_project_request = Runtime::API::Request.new(@api_client, '/projects')
- post create_project_request.url, path: project_name, name: project_name
+ it 'user creates a project with a file and deletes them afterwards', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347745' do
+ create_project_request = Runtime::API::Request.new(@api_client, '/projects')
+ post create_project_request.url, path: project_name, name: project_name
- expect_status(201)
- expect(json_body).to match(
- a_hash_including(name: project_name, path: project_name)
- )
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(name: project_name, path: project_name)
+ )
- default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
+ default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
- create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
- post create_file_request.url, branch: default_branch, content: 'Hello world', commit_message: 'Add README.md'
+ create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
+ post create_file_request.url, branch: default_branch, content: 'Hello world', commit_message: 'Add README.md'
- expect_status(201)
- expect(json_body).to match(
- a_hash_including(branch: default_branch, file_path: 'README.md')
- )
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(branch: default_branch, file_path: 'README.md')
+ )
- get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: default_branch)
- get get_file_request.url
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: default_branch)
+ get get_file_request.url
- expect_status(200)
- expect(json_body).to match(
- a_hash_including(
- ref: default_branch,
- file_path: 'README.md', file_name: 'README.md',
- encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
+ expect_status(200)
+ expect(json_body).to match(
+ a_hash_including(
+ ref: default_branch,
+ file_path: 'README.md', file_name: 'README.md',
+ encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
+ )
)
- )
- delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: default_branch, commit_message: 'Remove README.md')
- delete delete_file_request.url
+ delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: default_branch, commit_message: 'Remove README.md')
+ delete delete_file_request.url
- expect_status(204)
+ expect_status(204)
- get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree")
- get get_tree_request.url
+ get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree")
+ get get_tree_request.url
- expect_status(200)
- expect(json_body).to eq([])
+ expect_status(200)
+ expect(json_body).to eq([])
- delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
- delete delete_project_request.url
+ delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
+ delete delete_project_request.url
- expect_status(202)
- expect(json_body).to match(
- a_hash_including(message: '202 Accepted')
- )
- end
+ expect_status(202)
+ expect(json_body).to match(
+ a_hash_including(message: '202 Accepted')
+ )
+ end
- describe 'raw file access' do
- let(:svg_file) do
- <<-SVG
+ describe 'raw file access' do
+ let(:svg_file) do
+ <<-SVG
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
@@ -74,44 +75,45 @@ module QA
alert("surprise");
</script>
</svg>
- SVG
- end
-
- it 'sets no-cache headers as expected', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347746' do
- create_project_request = Runtime::API::Request.new(@api_client, '/projects')
- post create_project_request.url, path: project_name, name: project_name
-
- default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
-
- create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
- post create_file_request.url, branch: default_branch, content: svg_file, commit_message: 'Add test.svg'
-
- get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: default_branch)
-
- 3.times do
- response = get get_file_request.url
-
- # Subsequent responses aren't cached, so headers should match from
- # request to request, especially a 200 response rather than a 304
- # (indicating a cached response.) Further, :content_disposition
- # should include `attachment` for all responses.
- #
- expect(response.headers[:cache_control]).to include("no-store")
- expect(response.headers[:cache_control]).to include("no-cache")
- expect(response.headers[:pragma]).to eq("no-cache")
- expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
- expect(response.headers[:content_disposition]).to include("attachment")
- expect(response.headers[:content_disposition]).not_to include("inline")
- expect(response.headers[:content_type]).to include("image/svg+xml")
+ SVG
end
- delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
- delete delete_project_request.url
-
- expect_status(202)
- expect(json_body).to match(
- a_hash_including(message: '202 Accepted')
- )
+ it 'sets no-cache headers as expected', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347746' do
+ create_project_request = Runtime::API::Request.new(@api_client, '/projects')
+ post create_project_request.url, path: project_name, name: project_name
+
+ default_branch = json_body[:default_branch].to_s.empty? ? Runtime::Env.default_branch : json_body[:default_branch]
+
+ create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg")
+ post create_file_request.url, branch: default_branch, content: svg_file, commit_message: 'Add test.svg'
+
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/test.svg/raw", ref: default_branch)
+
+ 3.times do
+ response = get get_file_request.url
+
+ # Subsequent responses aren't cached, so headers should match from
+ # request to request, especially a 200 response rather than a 304
+ # (indicating a cached response.) Further, :content_disposition
+ # should include `attachment` for all responses.
+ #
+ expect(response.headers[:cache_control]).to include("no-store")
+ expect(response.headers[:cache_control]).to include("no-cache")
+ expect(response.headers[:pragma]).to eq("no-cache")
+ expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
+ expect(response.headers[:content_disposition]).to include("attachment")
+ expect(response.headers[:content_disposition]).not_to include("inline")
+ expect(response.headers[:content_type]).to include("image/svg+xml")
+ end
+
+ delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ expect(json_body).to match(
+ a_hash_including(message: '202 Accepted')
+ )
+ end
end
end
end
diff --git a/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb b/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb
new file mode 100644
index 00000000000..2b48945137c
--- /dev/null
+++ b/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+module QA
+ # TODO:
+ # Remove FF :ci_trigger_forward_variables
+ # when https://gitlab.com/gitlab-org/gitlab/-/issues/355572 is closed
+ RSpec.describe 'Verify', :runner, feature_flag: {
+ name: 'ci_trigger_forward_variables',
+ scope: :global
+ } do
+ describe 'Pipeline API defined variable inheritance' do
+ include_context 'variable inheritance test prep'
+
+ before do
+ add_ci_file(downstream1_project, [downstream1_ci_file])
+ add_ci_file(upstream_project, [upstream_ci_file, upstream_child1_ci_file, upstream_child2_ci_file])
+
+ start_pipeline_via_api_with_variable
+
+ Support::Waiter.wait_until(max_duration: 180, sleep_interval: 5) do
+ upstream_pipeline.status == 'success'
+ end
+
+ Support::Waiter.wait_until(max_duration: 180, sleep_interval: 5) do
+ downstream1_pipeline.pipeline_variables && child1_pipeline.pipeline_variables
+ end
+ end
+
+ it(
+ 'is determined based on forward:pipeline_variables condition',
+ :aggregate_failures,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360745',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361400', type: :investigating }
+ ) do
+ # Is inheritable when true
+ expect(child1_pipeline).to have_variable(key: key, value: value),
+ "Expected to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
+ " but got #{child1_pipeline.pipeline_variables}"
+
+ # Is not inheritable when false
+ expect(child2_pipeline).not_to have_variable(key: key, value: value),
+ "Did not expect to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
+ " but got #{child2_pipeline.pipeline_variables}"
+
+ # Is not inheritable by default
+ expect(downstream1_pipeline).not_to have_variable(key: key, value: value),
+ "Did not expect to find `{key: 'TEST_VAR', value: 'This is great!'}`" \
+ " but got #{downstream1_pipeline.pipeline_variables}"
+ end
+
+ def start_pipeline_via_api_with_variable
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = upstream_project
+ pipeline.variables = [{ key: key, value: value, variable_type: 'env_var' }]
+ end
+
+ Support::Waiter.wait_until { upstream_project.pipelines.size > 1 }
+ end
+
+ def upstream_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = upstream_project
+ pipeline.id = upstream_project.pipelines.first[:id]
+ end
+ end
+
+ def child1_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = upstream_project
+ pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'child1_trigger')
+ end
+ end
+
+ def child2_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = upstream_project
+ pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'child2_trigger')
+ end
+ end
+
+ def downstream1_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = downstream1_project
+ pipeline.id = upstream_pipeline.downstream_pipeline_id(bridge_name: 'downstream1_trigger')
+ end
+ end
+
+ def upstream_ci_file
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ stages:
+ - test
+ - deploy
+
+ child1_trigger:
+ stage: test
+ trigger:
+ include: .child1-ci.yml
+ forward:
+ pipeline_variables: true
+
+ child2_trigger:
+ stage: test
+ trigger:
+ include: .child2-ci.yml
+ forward:
+ pipeline_variables: false
+
+ # default behavior
+ downstream1_trigger:
+ stage: deploy
+ trigger:
+ project: #{downstream1_project.full_path}
+ YAML
+ }
+ end
+ end
+ end
+end
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 3a1a3ff7169..d7207803d45 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
@@ -3,7 +3,7 @@
require 'airborne'
module QA
- RSpec.describe 'Package', only: { subdomain: %i[staging pre] } do
+ RSpec.describe 'Package', only: { subdomain: %i[staging pre] }, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/360466', type: :investigating } do
include Support::API
describe 'Container Registry' do
@@ -57,7 +57,6 @@ module QA
MEDIA_TYPE: 'application/vnd.docker.distribution.manifest.v2+json'
before_script:
- token=$(curl -u "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" "https://$CI_SERVER_HOST/jwt/auth?service=container_registry&scope=repository:$CI_PROJECT_PATH:pull,push,delete" | jq -r '.token')
- - echo $token
script:
- 'digest=$(curl -L -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/manifests/master" | jq -r ".layers[0].digest")'
- 'curl -L -X DELETE -H "Authorization: Bearer $token" -H "Accept: $MEDIA_TYPE" "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH/blobs/$digest"'
diff --git a/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb b/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb
index bb4e0d71710..72090306fd9 100644
--- a/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb
+++ b/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_default_enabled_spec.rb
@@ -1,19 +1,21 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Service ping default enabled' do
- context 'When using default enabled from gitlab.yml config', :requires_admin, except: { job: 'review-qa-*' } do
- before do
- Flow::Login.sign_in_as_admin
+ RSpec.describe 'Non-devops' do
+ describe 'Service ping default enabled' do
+ context 'when using default enabled from gitlab.yml config', :requires_admin, except: { job: 'review-qa-*' } do
+ before do
+ Flow::Login.sign_in_as_admin
- Page::Main::Menu.perform(&:go_to_admin_area)
- Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
- end
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
+ end
- it 'has service ping toggle enabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348335' do
- Page::Admin::Settings::MetricsAndProfiling.perform do |setting|
- setting.expand_usage_statistics do |page|
- expect(page).not_to have_disabled_usage_data_checkbox
+ it 'has service ping toggle enabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348335' do
+ Page::Admin::Settings::MetricsAndProfiling.perform do |setting|
+ setting.expand_usage_statistics do |page|
+ expect(page).not_to have_disabled_usage_data_checkbox
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_disabled_spec.rb b/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_disabled_spec.rb
index cab8bd367f5..791bd688cea 100644
--- a/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_disabled_spec.rb
+++ b/qa/qa/specs/features/browser_ui/14_non_devops/service_ping_disabled_spec.rb
@@ -1,19 +1,21 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Service ping disabled', :orchestrated, :service_ping_disabled, :requires_admin do
- context 'when disabled from gitlab.yml config' do
- before do
- Flow::Login.sign_in_as_admin
+ RSpec.describe 'Non-devops' do
+ describe 'Service ping disabled', :orchestrated, :service_ping_disabled, :requires_admin do
+ context 'when disabled from gitlab.yml config' do
+ before do
+ Flow::Login.sign_in_as_admin
- Page::Main::Menu.perform(&:go_to_admin_area)
- Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
- end
+ Page::Main::Menu.perform(&:go_to_admin_area)
+ Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
+ end
- it 'has service ping toggle is disabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348340' do
- Page::Admin::Settings::MetricsAndProfiling.perform do |settings|
- settings.expand_usage_statistics do |usage_statistics|
- expect(usage_statistics).to have_disabled_usage_data_checkbox
+ it 'has service ping toggle is disabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348340' do
+ Page::Admin::Settings::MetricsAndProfiling.perform do |settings|
+ settings.expand_usage_statistics do |usage_statistics|
+ expect(usage_statistics).to have_disabled_usage_data_checkbox
+ end
end
end
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 3bf5a11b074..0477a9b8a1f 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
@@ -46,7 +46,7 @@ module QA
import_page.import!(github_repo, group.full_path, imported_project.name)
aggregate_failures do
- expect(import_page).to have_imported_project(github_repo)
+ expect(import_page).to have_imported_project(github_repo, wait: 240)
# validate button is present instead of navigating to avoid dealing with multiple tabs
# which makes the test more complicated
expect(import_page).to have_go_to_project_button(github_repo)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb
index c2bd61155b1..381a25a14d0 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/project_access_token_spec.rb
@@ -2,8 +2,8 @@
module QA
RSpec.describe 'Manage' do
- describe 'Project access tokens' do
- let(:project_access_token) {QA::Resource::ProjectAccessToken.fabricate_via_browser_ui!}
+ describe 'Project access tokens', :reliable do
+ let(:project_access_token) { QA::Resource::ProjectAccessToken.fabricate_via_browser_ui! }
it 'can be created and revoked via the UI', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347688' do
expect(project_access_token.token).not_to be_nil
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 eb4eb1d3e71..5fc9f8c2f41 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
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # TODO: Remove :requires_admin when the `Runtime::Feature.enable` method call is removed
- RSpec.describe 'Plan', :smoke, :requires_admin do
+ RSpec.describe 'Plan', :smoke, feature_flag: { name: 'vue_issues_list', scope: :group } do
describe 'Issue creation' do
let(:project) { Resource::Project.fabricate_via_api! }
let(:closed_issue) { Resource::Issue.fabricate_via_api! { |issue| issue.project = project } }
diff --git a/qa/qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb b/qa/qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb
index b7284f972ef..5f896c7bf10 100644
--- a/qa/qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/design_management/add_design_content_spec.rb
@@ -12,7 +12,7 @@ module QA
Flow::Login.sign_in
end
- it 'user adds a design and annotates it', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/352746', type: :investigating }, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347822' do
+ it 'user adds a design and annotates it', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347822' do
issue.visit!
Page::Project::Issue::Show.perform do |issue|
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_from_push_notification_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_from_push_notification_spec.rb
new file mode 100644
index 00000000000..10d7e0b071f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_from_push_notification_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Create a new merge request from the event notification after a push' do
+ let(:branch_name) { "merge-request-test-#{SecureRandom.hex(8)}" }
+ let(:title) { "Merge from push event notification test #{SecureRandom.hex(8)}" }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ it(
+ 'creates a merge request after a push via the git CLI',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360489'
+ ) do
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = branch_name
+ end
+
+ project.visit!
+ Page::Project::Show.perform(&:new_merge_request)
+ Page::MergeRequest::New.perform do |merge_request|
+ merge_request.fill_title(title)
+ merge_request.create_merge_request
+ end
+
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_title(title)
+ end
+ end
+
+ it(
+ 'creates a merge request after a push via the API',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360490'
+ ) do
+ commit = Resource::Repository::Commit.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.add_files([{ 'file_path': "file-#{SecureRandom.hex(8)}.txt", 'content': 'MR init' }])
+ resource.branch = branch_name
+ resource.start_branch = project.default_branch
+ end
+ project.wait_for_push(commit.commit_message)
+
+ project.visit!
+ Page::Project::Show.perform(&:new_merge_request)
+ Page::MergeRequest::New.perform do |merge_request|
+ merge_request.fill_title(title)
+ merge_request.create_merge_request
+ end
+
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_title(title)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
index 6a2fe705cf7..f4ed9f28dac 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
@@ -24,7 +24,6 @@ module QA
Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request|
merge_request.project = project
merge_request.title = merge_request_title
- merge_request.assignee = 'me'
merge_request.description = merge_request_description
end
@@ -38,7 +37,7 @@ module QA
'creates a merge request with a milestone and label',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347762'
) do
- gitlab_account_username = "@#{Runtime::User.username}"
+ gitlab_account_user_name = Resource::User.default.reload!.name
milestone = Resource::ProjectMilestone.fabricate_via_api! do |milestone|
milestone.project = project
@@ -54,14 +53,13 @@ module QA
merge_request.description = merge_request_description
merge_request.project = project
merge_request.milestone = milestone
- merge_request.assignee = 'me'
merge_request.labels.push(label)
end
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_title(merge_request_title)
expect(merge_request).to have_description(merge_request_description)
- expect(merge_request).to have_assignee(gitlab_account_username)
+ expect(merge_request).to have_assignee(gitlab_account_user_name)
expect(merge_request).to have_label(label.title)
expect(merge_request).to have_milestone(milestone.title)
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 d1e852979d0..b544c9aa211 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
@@ -5,7 +5,7 @@ module QA
describe 'Merge request creation from fork', quarantine: {
only: :production,
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/343801",
- type: :investigation
+ type: :investigating
} do
let(:merge_request) do
Resource::MergeRequestFromFork.fabricate_via_browser_ui! do |merge_request|
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb
index 38adf2f5d55..b20d0929e9c 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', :reliable do
context 'Add batch suggestions to a Merge Request' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 812a7bae6c3..0e4aa67162f 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
@@ -1,32 +1,27 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :requires_admin do
+ # This test modifies an instance level setting,
+ # so skipping on live envs to avoid random transient issues
+ RSpec.describe 'Create', :requires_admin, :skip_live_env do
describe 'push after setting the file size limit via admin/application_settings' do
- # Note: The file size limits in this test should be greater than the limits in
- # ee/browser_ui/3_create/repository/push_rules_spec to prevent that test from
- # triggering the limit set in this test (which can happen on Staging where the
- # tests are run in parallel).
- # See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705
-
include Support::API
- before(:context) do
- @project = Resource::Project.fabricate_via_api! do |p|
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |p|
p.name = 'project-test-push-limit'
p.initialize_with_readme = true
end
-
- @api_client = Runtime::API::Client.as_admin
end
after(:context) do
- # need to set the default value after test
- # default value for file size limit is empty
set_file_size_limit(nil)
end
- it 'push successful when the file size is under the limit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347758' do
+ it(
+ 'push successful when the file size is under the limit',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347758'
+ ) do
set_file_size_limit(5)
retry_on_fail do
@@ -36,7 +31,10 @@ module QA
end
end
- it 'push fails when the file size is above the limit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347759' do
+ it(
+ 'push fails when the file size is above the limit',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347759'
+ ) do
set_file_size_limit(2)
retry_on_fail do
@@ -46,7 +44,7 @@ module QA
end
def set_file_size_limit(limit)
- request = Runtime::API::Request.new(@api_client, '/application/settings')
+ request = Runtime::API::Request.new(Runtime::API::Client.as_admin, '/application/settings')
response = put request.url, receive_max_input_size: limit
expect(response.code).to eq(200)
@@ -56,13 +54,13 @@ module QA
def push_new_file(file_name, wait_for_push: true)
commit_message = 'Adding a new file'
output = Resource::Repository::Push.fabricate! do |p|
- p.repository_http_uri = @project.repository_http_location.uri
+ p.repository_http_uri = project.repository_http_location.uri
p.file_name = file_name
p.file_content = SecureRandom.random_bytes(3000000)
p.commit_message = commit_message
p.new_branch = false
end
- @project.wait_for_push commit_message
+ project.wait_for_push commit_message
output
end
@@ -77,10 +75,8 @@ module QA
# under a minute, i.e., in fewer than 6 attempts with a 10 second sleep
# between attempts.
# See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/30233#note_188616863
- def retry_on_fail
- Support::Retrier.retry_on_exception(max_attempts: 6, reload_page: nil, sleep_interval: 10) do
- yield
- end
+ def retry_on_fail(&block)
+ Support::Retrier.retry_on_exception(max_attempts: 6, reload_page: false, sleep_interval: 10, &block)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
index f8129c9ccba..91b0940f137 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', :reliable do
describe 'Protected branch support' do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
index b0eb3ac7b37..a1a795ce0f3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
@@ -1,34 +1,36 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'SSH keys support', :smoke, :skip_fips_env do
- key_title = "key for ssh tests #{Time.now.to_f}"
- key = nil
+ RSpec.describe 'Create' do
+ describe 'SSH keys support', :smoke, :skip_fips_env do
+ key_title = "key for ssh tests #{Time.now.to_f}"
+ key = nil
- before do
- Flow::Login.sign_in
- end
+ before do
+ Flow::Login.sign_in
+ end
+
+ it 'user can add an SSH key', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347819' do
+ key = Resource::SSHKey.fabricate_via_browser_ui! do |resource|
+ resource.title = key_title
+ end
- it 'user can add an SSH key', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347819' do
- key = Resource::SSHKey.fabricate_via_browser_ui! do |resource|
- resource.title = key_title
+ expect(page).to have_content(key.title)
+ expect(page).to have_content(key.md5_fingerprint)
end
- expect(page).to have_content(key.title)
- expect(page).to have_content(key.md5_fingerprint)
- end
+ # Note this context ensures that the example it contains is executed after the example above. Be aware of the order of execution if you add new examples in either context.
+ context 'after adding an ssh key' do
+ it 'can delete an ssh key', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347820' do
+ Page::Main::Menu.perform(&:click_edit_profile_link)
+ Page::Profile::Menu.perform(&:click_ssh_keys)
+ Page::Profile::SSHKeys.perform do |ssh_keys|
+ ssh_keys.remove_key(key.title)
+ end
- # Note this context ensures that the example it contains is executed after the example above. Be aware of the order of execution if you add new examples in either context.
- context 'after adding an ssh key' do
- it 'can delete an ssh key', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347820' do
- Page::Main::Menu.perform(&:click_edit_profile_link)
- Page::Profile::Menu.perform(&:click_ssh_keys)
- Page::Profile::SSHKeys.perform do |ssh_keys|
- ssh_keys.remove_key(key.title)
+ expect(page).not_to have_content("Title: #{key.title}")
+ expect(page).not_to have_content(key.md5_fingerprint)
end
-
- expect(page).not_to have_content("Title: #{key.title}")
- expect(page).not_to have_content(key.md5_fingerprint)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb
index 0b63d9a1edb..a50b995e483 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', :reliable do
describe 'Multiple file snippet' do
let(:first_file_content) { 'First file content' }
let(:second_file_content) { 'Second file content' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb
index b45624381c8..4b8edf8b792 100644
--- a/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/content_editor_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Create', :reliable do
context 'Content Editor' do
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
let(:page_title) { 'Content Editor Page' }
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
new file mode 100644
index 00000000000..63801536c34
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner do
+ describe 'Pipeline with customizable variable' do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+ let(:pipeline_job_name) { 'customizable-variable' }
+ let(:variable_custom_value) { 'Custom Foo' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-customizable-variable-pipeline'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ 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: <<~YAML
+ variables:
+ FOO:
+ value: "Default Foo"
+ description: "This is a description for the foo variable"
+ #{pipeline_job_name}:
+ tags:
+ - #{executor}
+ script: echo "$FOO"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform do |index|
+ index.click_run_pipeline_button
+ end
+ end
+
+ after do
+ [runner, project].each(&:remove_via_api!)
+ end
+
+ it(
+ 'manually creates a pipeline and uses the defined custom variable value',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/361814'
+ ) do
+ Page::Project::Pipeline::New.perform do |new|
+ new.configure_variable(value: variable_custom_value)
+ new.click_run_pipeline_button
+ end
+
+ Page::Project::Pipeline::Show.perform do |show|
+ Support::Waiter.wait_until { show.passed? }
+ end
+
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.id = project.job_by_name(pipeline_job_name)[:id]
+ job.name = pipeline_job_name
+ job.project = project
+ end
+
+ job.visit!
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content(variable_custom_value)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb
index 496cc5f8a60..bd200e57ff9 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb
@@ -24,7 +24,8 @@ module QA
it(
'is inheritable when forward:pipeline_variables is true',
:aggregate_failures,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358197'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358197',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361338', type: :investigating }
) do
visit_job_page('child1', 'child1_job')
verify_job_log_shows_variable_value
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb
index 2a0aaf6d7a3..2bd0be542fe 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb
@@ -25,7 +25,8 @@ module QA
it(
'is not inheritable when forward:pipeline_variables is false',
:aggregate_failures,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358199'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358199',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361339', type: :investigating }
) do
visit_job_page('child1', 'child1_job')
verify_job_log_does_not_show_variable_value
@@ -39,7 +40,8 @@ module QA
it(
'is not inheritable by default',
:aggregate_failures,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358200'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358200',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361339', type: :investigating }
) do
visit_job_page('child2', 'child2_job')
verify_job_log_does_not_show_variable_value
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/multi-project_pipelines_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/multi-project_pipelines_spec.rb
index 0d8756fc9a3..c5408f30d16 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/multi-project_pipelines_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/multi-project_pipelines_spec.rb
@@ -44,7 +44,7 @@ module QA
end
it(
- 'creates a multi-project pipeline',
+ 'creates a multi-project pipeline with artifact download',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358064'
) do
Page::Project::Pipeline::Show.perform do |show|
@@ -78,7 +78,10 @@ module QA
job1:
stage: test
tags: ["#{executor}"]
- script: echo 'done'
+ script: echo 'done' > output.txt
+ artifacts:
+ paths:
+ - output.txt
staging:
stage: deploy
@@ -96,7 +99,12 @@ module QA
"#{downstream_job_name}":
stage: test
tags: ["#{executor}"]
- script: echo 'done'
+ needs:
+ - project: #{upstream_project.path_with_namespace}
+ job: job1
+ ref: main
+ artifacts: true
+ script: cat output.txt
YAML
}
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_dependent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
index 5b7a569fa8a..f2278c7bf6d 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
@@ -60,7 +60,14 @@ module QA
child_job:
stage: test
tags: ["#{project.name}"]
- script: echo "Child job done!"
+ needs:
+ - project: #{project.path_with_namespace}
+ job: job1
+ ref: main
+ artifacts: true
+ script:
+ - cat output.txt
+ - echo "Child job done!"
YAML
}
@@ -84,18 +91,28 @@ module QA
file_path: '.gitlab-ci.yml',
content: <<~YAML
stages:
+ - build
- test
- deploy
+ default:
+ tags: ["#{project.name}"]
+
job1:
+ stage: build
+ script: echo "build success" > output.txt
+ artifacts:
+ paths:
+ - output.txt
+
+ job2:
stage: test
trigger:
include: ".child-ci.yml"
strategy: depend
- job2:
+ job3:
stage: deploy
- tags: ["#{project.name}"]
script: echo "parent deploy done"
YAML
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
index 8d2af3ea0df..ac91a9dd2d3 100644
--- 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
@@ -73,7 +73,7 @@ module QA
show.select_branch_from_dropdown(random_test_string)
aggregate_failures do
- expect(show.target_branch_name).to eq(random_test_string), 'Target branch field is not showing expected branch name.'
+ expect(show.source_branch_name).to eq(random_test_string), 'Branch field is not showing expected branch name.'
expect(show.editing_content).to have_content(random_test_string), 'Editor content does not include expected test string.'
end
@@ -81,7 +81,7 @@ module QA
show.select_branch_from_dropdown(project.default_branch)
aggregate_failures do
- expect(show.target_branch_name).to eq(project.default_branch), 'Target branch field is not showing expected branch name.'
+ expect(show.source_branch_name).to eq(project.default_branch), 'Branch field is not showing expected branch name.'
expect(show.editing_content).to have_content(project.default_branch), 'Editor content does not include expected test string.'
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_can_create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_can_create_merge_request_spec.rb
index b9f616aa733..931bb97ba32 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_can_create_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_can_create_merge_request_spec.rb
@@ -34,8 +34,8 @@ module QA
expect(show).to have_no_new_mr_checkbox
end
- # The new MR checkbox is visible after a new target branch name is set
- show.set_target_branch(SecureRandom.hex(10))
+ # The new MR checkbox is visible after a new branch name is set
+ show.set_source_branch(SecureRandom.hex(10))
expect(show).to have_new_mr_checkbox
show.select_new_mr_checkbox
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb
index 205b4d1168a..d03ebd5aba3 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb
@@ -31,7 +31,7 @@ module QA
project.remove_via_api!
end
- it 'creates 2 trigger jobs and passes corresponding matrix variables', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348000' do
+ it 'creates 2 trigger jobs and passes corresponding matrix variables', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348000', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/361346', type: :investigating } do
Page::Project::Pipeline::Show.perform do |parent_pipeline|
trigger_title1 = 'deploy: [ovh, monitoring]'
trigger_title2 = 'deploy: [ovh, app]'
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb
index 00c5d4c74d4..d34df17c477 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb
@@ -53,13 +53,13 @@ module QA
it 'creates new pipeline and target branch', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349005' do
Page::Project::PipelineEditor::Show.perform do |show|
show.write_to_editor(random_test_string)
- show.set_target_branch(random_test_string)
+ show.set_source_branch(random_test_string)
show.submit_changes
Support::Waiter.wait_until { project.pipelines.size > 1 }
aggregate_failures do
- expect(show.target_branch_name).to eq(random_test_string)
+ expect(show.source_branch_name).to eq(random_test_string)
expect(show.current_branch).to eq(random_test_string)
expect(show.editing_content).to have_content(random_test_string)
expect { show.pipeline_id }.to eventually_eq(project.pipelines.pluck(:id).max).within(max_duration: 60, sleep_interval: 3)
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 f44c56ca0f9..122fb0fc1a0 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
@@ -3,7 +3,6 @@
module QA
RSpec.describe 'Verify', :runner do
describe 'Code coverage statistics' do
- let(:simplecov) { '\(\d+.\d+\%\) covered' }
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:runner) do
Resource::Runner.fabricate_via_api! do |runner|
@@ -19,8 +18,9 @@ module QA
mr.file_content = <<~EOF
test:
tags: [e2e-test]
+ coverage: '/\\d+\\.\\d+% covered/'
script:
- - echo '(66.67%) covered'
+ - echo '66.67% covered'
EOF
end
end
@@ -34,8 +34,6 @@ module QA
end
it 'creates an MR with code coverage statistics', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348068' do
- runner.project.visit!
- configure_code_coverage(simplecov)
merge_request.visit!
Page::MergeRequest::Show.perform do |mr_widget|
@@ -44,16 +42,5 @@ module QA
end
end
end
-
- private
-
- def configure_code_coverage(coverage_tool_pattern)
- Page::Project::Menu.perform(&:go_to_ci_cd_settings)
- Page::Project::Settings::CiCd.perform do |settings|
- settings.expand_general_pipelines do |coverage|
- coverage.configure_coverage_regex(coverage_tool_pattern)
- end
- end
- end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb b/qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb
index 9a5a5416d5c..d9d75a8ae7a 100644
--- a/qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/dependency_proxy/dependency_proxy_spec.rb
@@ -39,6 +39,7 @@ module QA
end
after do
+ project.remove_via_api!
runner.remove_via_api!
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
index 9ef5b8c84fa..921b36b34af 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable do
describe 'Maven group level endpoint' do
include Runtime::Fixtures
include_context 'packages registry qa scenario'
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
index d79f65764d4..13607ba1b41 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable do
describe 'Maven project level endpoint' do
let(:group_id) { 'com.gitlab.qa' }
let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" }
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb
index 0ee5f1b6a0b..61a92daf129 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb
@@ -1,170 +1,170 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package Registry', :orchestrated, :reliable, :packages, :object_storage do
- describe 'npm instance level endpoint' do
- using RSpec::Parameterized::TableSyntax
- include Runtime::Fixtures
- include Support::Helpers::MaskToken
-
- let!(:registry_scope) { Runtime::Namespace.sandbox_name }
- let!(:personal_access_token) do
- unless Page::Main::Menu.perform(&:signed_in?)
- Flow::Login.sign_in
+ RSpec.describe 'Package' do
+ describe 'Package Registry', :orchestrated, :reliable, :packages, :object_storage do
+ describe 'npm instance level endpoint' do
+ using RSpec::Parameterized::TableSyntax
+ include Runtime::Fixtures
+ include Support::Helpers::MaskToken
+
+ let!(:registry_scope) { Runtime::Namespace.sandbox_name }
+ let!(:personal_access_token) do
+ Flow::Login.sign_in unless Page::Main::Menu.perform(&:signed_in?)
+
+ Resource::PersonalAccessToken.fabricate!.token
end
- Resource::PersonalAccessToken.fabricate!.token
- end
-
- let(:project_deploy_token) do
- Resource::ProjectDeployToken.fabricate_via_api! do |deploy_token|
- deploy_token.name = 'npm-deploy-token'
- deploy_token.project = project
- deploy_token.scopes = %w[
- read_repository
- read_package_registry
- write_package_registry
- ]
+ let(:project_deploy_token) do
+ Resource::ProjectDeployToken.fabricate_via_api! do |deploy_token|
+ deploy_token.name = 'npm-deploy-token'
+ deploy_token.project = project
+ deploy_token.scopes = %w[
+ read_repository
+ read_package_registry
+ write_package_registry
+ ]
+ end
end
- end
- let(:uri) { URI.parse(Runtime::Scenario.gitlab_address) }
- let(:gitlab_address_with_port) { "#{uri.scheme}://#{uri.host}:#{uri.port}" }
- let(:gitlab_host_with_port) { "#{uri.host}:#{uri.port}" }
+ let(:uri) { URI.parse(Runtime::Scenario.gitlab_address) }
+ let(:gitlab_address_with_port) { "#{uri.scheme}://#{uri.host}:#{uri.port}" }
+ let(:gitlab_host_with_port) { "#{uri.host}:#{uri.port}" }
- let!(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'npm-instace-level-publish'
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'npm-instace-level-publish'
+ end
end
- end
- let!(:another_project) do
- Resource::Project.fabricate_via_api! do |another_project|
- another_project.name = 'npm-instance-level-install'
- another_project.template_name = 'express'
- another_project.group = project.group
+ let!(:another_project) do
+ Resource::Project.fabricate_via_api! do |another_project|
+ another_project.name = 'npm-instance-level-install'
+ another_project.template_name = 'express'
+ another_project.group = project.group
+ end
end
- end
- let!(:runner) do
- Resource::Runner.fabricate! do |runner|
- runner.name = "qa-runner-#{Time.now.to_i}"
- runner.tags = ["runner-for-#{project.group.name}"]
- runner.executor = :docker
- runner.token = project.group.reload!.runners_token
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.group.name}"]
+ runner.executor = :docker
+ runner.token = project.group.reload!.runners_token
+ end
end
- end
- let(:package) do
- Resource::Package.init do |package|
- package.name = "@#{registry_scope}/#{project.name}-#{SecureRandom.hex(8)}"
- package.project = project
+ let(:package) do
+ Resource::Package.init do |package|
+ package.name = "@#{registry_scope}/#{project.name}-#{SecureRandom.hex(8)}"
+ package.project = project
+ end
end
- end
-
- after do
- package.remove_via_api!
- runner.remove_via_api!
- project.remove_via_api!
- another_project.remove_via_api!
- end
- where(:case_name, :authentication_token_type, :token_name, :testcase) do
- 'using personal access token' | :personal_access_token | 'Personal Access Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347600'
- 'using ci job token' | :ci_job_token | 'CI Job Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347599'
- 'using project deploy token' | :project_deploy_token | 'Deploy Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347598'
- end
+ after do
+ package.remove_via_api!
+ runner.remove_via_api!
+ project.remove_via_api!
+ another_project.remove_via_api!
+ end
- with_them do
- let(:auth_token) do
- case authentication_token_type
- when :personal_access_token
- use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: project)
- use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: another_project)
- when :ci_job_token
- '${CI_JOB_TOKEN}'
- when :project_deploy_token
- use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: project)
- use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: another_project)
- end
+ where(:case_name, :authentication_token_type, :token_name, :testcase) do
+ 'using personal access token' | :personal_access_token | 'Personal Access Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347600'
+ 'using ci job token' | :ci_job_token | 'CI Job Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347599'
+ 'using project deploy token' | :project_deploy_token | 'Deploy Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347598'
end
- it 'push and pull a npm package via CI', testcase: params[:testcase] do
- Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
- npm_upload_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_package_instance.yaml.erb')).result(binding)
- package_json = ERB.new(read_fixture('package_managers/npm', 'package_instance.json.erb')).result(binding)
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'Add files'
- commit.add_files([
- {
- file_path: '.gitlab-ci.yml',
- content: npm_upload_yaml
- },
- {
- file_path: 'package.json',
- content: package_json
- }
- ])
+ with_them do
+ let(:auth_token) do
+ case authentication_token_type
+ when :personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: project)
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: another_project)
+ when :ci_job_token
+ '${CI_JOB_TOKEN}'
+ when :project_deploy_token
+ use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: project)
+ use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: another_project)
end
end
- project.visit!
- Flow::Pipeline.visit_latest_pipeline
+ it 'push and pull a npm package via CI', testcase: params[:testcase] do
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ npm_upload_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_package_instance.yaml.erb')).result(binding)
+ package_json = ERB.new(read_fixture('package_managers/npm', 'package_instance.json.erb')).result(binding)
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add files'
+ commit.add_files([
+ {
+ file_path: '.gitlab-ci.yml',
+ content: npm_upload_yaml
+ },
+ {
+ file_path: 'package.json',
+ content: package_json
+ }
+ ])
+ end
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('deploy')
- end
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
- end
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('deploy')
+ end
- Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- npm_install_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_install_package_instance.yaml.erb')).result(binding)
-
- commit.project = another_project
- commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files([
- {
- file_path: '.gitlab-ci.yml',
- content: npm_install_yaml
- }
- ])
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
end
- end
- another_project.visit!
- Flow::Pipeline.visit_latest_pipeline
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ npm_install_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_install_package_instance.yaml.erb')).result(binding)
+
+ commit.project = another_project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([
+ {
+ file_path: '.gitlab-ci.yml',
+ content: npm_install_yaml
+ }
+ ])
+ end
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('install')
- end
+ another_project.visit!
+ Flow::Pipeline.visit_latest_pipeline
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
- job.click_browse_button
- end
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('install')
+ end
- Page::Project::Artifact::Show.perform do |artifacts|
- artifacts.go_to_directory('node_modules')
- artifacts.go_to_directory("@#{registry_scope}")
- expect(artifacts).to have_content("#{project.name}")
- end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ job.click_browse_button
+ end
+
+ Page::Project::Artifact::Show.perform do |artifacts|
+ artifacts.go_to_directory('node_modules')
+ artifacts.go_to_directory("@#{registry_scope}")
+ expect(artifacts).to have_content(project.name.to_s)
+ end
- project.visit!
- Page::Project::Menu.perform(&:click_packages_link)
+ project.visit!
+ Page::Project::Menu.perform(&:click_packages_link)
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_package(package.name)
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package.name)
- index.click_package(package.name)
- end
+ index.click_package(package.name)
+ end
- Page::Project::Packages::Show.perform do |show|
- expect(show).to have_package_info(package.name, "1.0.0")
+ Page::Project::Packages::Show.perform do |show|
+ expect(show).to have_package_info(package.name, "1.0.0")
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb
index 5ebcb94d0d0..59324c7338a 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/npm/npm_project_level_spec.rb
@@ -1,142 +1,142 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package Registry', :orchestrated, :reliable, :packages, :object_storage do
- describe 'npm project level endpoint' do
- using RSpec::Parameterized::TableSyntax
- include Runtime::Fixtures
- include Support::Helpers::MaskToken
-
- let!(:registry_scope) { Runtime::Namespace.sandbox_name }
- let!(:personal_access_token) do
- unless Page::Main::Menu.perform(&:signed_in?)
- Flow::Login.sign_in
+ RSpec.describe 'Package' do
+ describe 'Package Registry', :orchestrated, :reliable, :packages, :object_storage do
+ describe 'npm project level endpoint' do
+ using RSpec::Parameterized::TableSyntax
+ include Runtime::Fixtures
+ include Support::Helpers::MaskToken
+
+ let!(:registry_scope) { Runtime::Namespace.sandbox_name }
+ let!(:personal_access_token) do
+ Flow::Login.sign_in unless Page::Main::Menu.perform(&:signed_in?)
+
+ Resource::PersonalAccessToken.fabricate!.token
end
- Resource::PersonalAccessToken.fabricate!.token
- end
-
- let(:project_deploy_token) do
- Resource::ProjectDeployToken.fabricate_via_api! do |deploy_token|
- deploy_token.name = 'npm-deploy-token'
- deploy_token.project = project
- deploy_token.scopes = %w[
- read_repository
- read_package_registry
- write_package_registry
- ]
- end
- end
-
- let(:uri) { URI.parse(Runtime::Scenario.gitlab_address) }
- let(:gitlab_address_with_port) { "#{uri.scheme}://#{uri.host}:#{uri.port}" }
- let(:gitlab_host_with_port) { "#{uri.host}:#{uri.port}" }
-
- let!(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'npm-project-level'
- project.visibility = :private
- 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
-
- let(:package) do
- Resource::Package.init do |package|
- package.name = "@#{registry_scope}/mypackage-#{SecureRandom.hex(8)}"
- package.project = project
- end
- end
-
- after do
- package.remove_via_api!
- runner.remove_via_api!
- project.remove_via_api!
- end
-
- where(:case_name, :authentication_token_type, :token_name, :testcase) do
- 'using personal access token' | :personal_access_token | 'Personal Access Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347592'
- 'using ci job token' | :ci_job_token | 'CI Job Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347594'
- 'using project deploy token' | :project_deploy_token | 'Deploy Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347593'
- end
-
- with_them do
- let(:auth_token) do
- case authentication_token_type
- when :personal_access_token
- use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: project)
- when :ci_job_token
- '${CI_JOB_TOKEN}'
- when :project_deploy_token
- use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: project)
+ let(:project_deploy_token) do
+ Resource::ProjectDeployToken.fabricate_via_api! do |deploy_token|
+ deploy_token.name = 'npm-deploy-token'
+ deploy_token.project = project
+ deploy_token.scopes = %w[
+ read_repository
+ read_package_registry
+ write_package_registry
+ ]
end
end
- it 'push and pull a npm package via CI', testcase: params[:testcase] do
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- npm_upload_install_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_install_package_project.yaml.erb')).result(binding)
- package_json = ERB.new(read_fixture('package_managers/npm', 'package_project.json.erb')).result(binding)
-
- commit.project = project
- commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files([
- {
- file_path: '.gitlab-ci.yml',
- content: npm_upload_install_yaml
- },
- {
- file_path: 'package.json',
- content: package_json
- }
- ])
- end
-
- project.visit!
- Flow::Pipeline.visit_latest_pipeline
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('deploy')
- end
+ let(:uri) { URI.parse(Runtime::Scenario.gitlab_address) }
+ let(:gitlab_address_with_port) { "#{uri.scheme}://#{uri.host}:#{uri.port}" }
+ let(:gitlab_host_with_port) { "#{uri.host}:#{uri.port}" }
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
- end
-
- Flow::Pipeline.visit_latest_pipeline
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('install')
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'npm-project-level'
+ project.visibility = :private
end
+ end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
- job.click_browse_button
+ 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
- Page::Project::Artifact::Show.perform do |artifacts|
- artifacts.go_to_directory('node_modules')
- artifacts.go_to_directory("@#{registry_scope}")
- expect(artifacts).to have_content('mypackage')
+ let(:package) do
+ Resource::Package.init do |package|
+ package.name = "@#{registry_scope}/mypackage-#{SecureRandom.hex(8)}"
+ package.project = project
end
+ end
- project.visit!
- Page::Project::Menu.perform(&:click_packages_link)
+ after do
+ package.remove_via_api!
+ runner.remove_via_api!
+ project.remove_via_api!
+ end
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_package(package.name)
+ where(:case_name, :authentication_token_type, :token_name, :testcase) do
+ 'using personal access token' | :personal_access_token | 'Personal Access Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347592'
+ 'using ci job token' | :ci_job_token | 'CI Job Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347594'
+ 'using project deploy token' | :project_deploy_token | 'Deploy Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347593'
+ end
- index.click_package(package.name)
+ with_them do
+ let(:auth_token) do
+ case authentication_token_type
+ when :personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: project)
+ when :ci_job_token
+ '${CI_JOB_TOKEN}'
+ when :project_deploy_token
+ use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: project)
+ end
end
- Page::Project::Packages::Show.perform do |show|
- expect(show).to have_package_info(package.name, "1.0.0")
+ it 'push and pull a npm package via CI', testcase: params[:testcase] do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ npm_upload_install_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_install_package_project.yaml.erb')).result(binding)
+ package_json = ERB.new(read_fixture('package_managers/npm', 'package_project.json.erb')).result(binding)
+
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([
+ {
+ file_path: '.gitlab-ci.yml',
+ content: npm_upload_install_yaml
+ },
+ {
+ file_path: 'package.json',
+ content: package_json
+ }
+ ])
+ end
+
+ project.visit!
+ 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
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('install')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ job.click_browse_button
+ end
+
+ Page::Project::Artifact::Show.perform do |artifacts|
+ artifacts.go_to_directory('node_modules')
+ artifacts.go_to_directory("@#{registry_scope}")
+ expect(artifacts).to have_content('mypackage')
+ end
+
+ project.visit!
+ 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, "1.0.0")
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb
index 0ddb59d6625..f229f30facc 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable do
describe 'NuGet group level endpoint' do
using RSpec::Parameterized::TableSyntax
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb
index d5fd78480d2..ab6896ca26f 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, quarantine: {
+ type: :flaky,
+ issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/361704"
+ } do
describe 'NuGet project level endpoint' do
include Support::Helpers::MaskToken
@@ -129,12 +132,12 @@ module QA
file_path: 'dotnetcore.csproj',
content: <<~EOF
<Project Sdk="Microsoft.NET.Sdk">
-
+
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
-
+
</Project>
EOF
}
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index e0cd5a52bfb..dca6f961047 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -1,7 +1,12 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Configure', only: { subdomain: :staging } do
+ RSpec.describe 'Configure',
+ only: { subdomain: :staging },
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1198',
+ type: :waiting_on
+ } do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'autodevops-project'
diff --git a/qa/spec/support/shared_contexts/merge_train_spec_with_user_prep.rb b/qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb
index 9d1a37cb0b8..9d1a37cb0b8 100644
--- a/qa/spec/support/shared_contexts/merge_train_spec_with_user_prep.rb
+++ b/qa/qa/specs/features/shared_contexts/merge_train_spec_with_user_prep.rb
diff --git a/qa/spec/support/shared_contexts/packages_registry_shared_context.rb b/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb
index 73a6c2bd99e..73a6c2bd99e 100644
--- a/qa/spec/support/shared_contexts/packages_registry_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb
diff --git a/qa/spec/support/shared_contexts/variable_inheritance_shared_context.rb b/qa/qa/specs/features/shared_contexts/variable_inheritance_shared_context.rb
index 1dc8870d4d9..fbe517f51f8 100644
--- a/qa/spec/support/shared_contexts/variable_inheritance_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/variable_inheritance_shared_context.rb
@@ -2,6 +2,8 @@
module QA
RSpec.shared_context 'variable inheritance test prep' do
+ let(:key) { 'TEST_VAR' }
+ let(:value) { 'This is great!' }
let(:random_string) { Faker::Alphanumeric.alphanumeric(number: 8) }
let(:group) do
@@ -57,7 +59,7 @@ module QA
Flow::Pipeline.wait_for_latest_pipeline
Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
Page::Project::Pipeline::New.perform do |new|
- new.add_variable('TEST_VAR', 'This is great!')
+ new.configure_variable(key: key, value: value)
new.click_run_pipeline_button
end
end
@@ -80,14 +82,14 @@ module QA
def verify_job_log_shows_variable_value
Page::Project::Job::Show.perform do |show|
show.wait_until { show.successful? }
- expect(show.output).to have_content('This is great!')
+ expect(show.output).to have_content(value)
end
end
def verify_job_log_does_not_show_variable_value
Page::Project::Job::Show.perform do |show|
show.wait_until { show.successful? }
- expect(show.output).to have_no_content('This is great!')
+ expect(show.output).to have_no_content(value)
end
end
diff --git a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
index 4bbad9bf3e5..4bbad9bf3e5 100644
--- a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
+++ b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
diff --git a/qa/qa/specs/knapsack_runner.rb b/qa/qa/specs/knapsack_runner.rb
new file mode 100644
index 00000000000..4908553e43d
--- /dev/null
+++ b/qa/qa/specs/knapsack_runner.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Specs
+ class KnapsackRunner
+ def self.run(args)
+ allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
+
+ Knapsack.logger.info '==== Knapsack specs to execute ====='
+ Knapsack.logger.info 'Report specs:'
+ Knapsack.logger.info allocator.report_node_tests
+ Knapsack.logger.info 'Leftover specs:'
+ Knapsack.logger.info allocator.leftover_node_tests
+
+ status = RSpec::Core::Runner.run([*args, '--', *allocator.node_tests])
+ yield status if block_given?
+ status
+ end
+ end
+ end
+end
diff --git a/qa/spec/qa_deprecation_toolkit_env.rb b/qa/qa/specs/qa_deprecation_toolkit_env.rb
index 2a21961d89e..21ef5a6f229 100644
--- a/qa/spec/qa_deprecation_toolkit_env.rb
+++ b/qa/qa/specs/qa_deprecation_toolkit_env.rb
@@ -7,9 +7,11 @@ require 'active_support/gem_version'
module QaDeprecationToolkitEnv
# Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
+ # rubocop:disable Layout/LineLength
def self.kwargs_warning
%r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z}
end
+ # rubocop:enable Layout/LineLength
def self.configure!
# Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index c30e5d822c4..68b624b3f2e 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -19,18 +19,6 @@ module QA
@options = []
end
- def paths_from_knapsack
- allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
-
- QA::Runtime::Logger.info '==== Knapsack specs to execute ====='
- QA::Runtime::Logger.info 'Report specs:'
- QA::Runtime::Logger.info allocator.report_node_tests.join(', ')
- QA::Runtime::Logger.info 'Leftover specs:'
- QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ')
-
- ['--', allocator.node_tests]
- end
-
def rspec_tags
tags_for_rspec = []
@@ -52,77 +40,79 @@ module QA
tags_for_rspec
end
- # rubocop:disable Metrics/AbcSize
- # rubocop:disable Metrics/CyclomaticComplexity
def perform
args = []
args.push('--tty') if tty
args.push(rspec_tags)
args.push(options)
- if Runtime::Env.knapsack?
- args.push(paths_from_knapsack)
- else
- args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
+ unless Runtime::Env.knapsack? || options.any? { |opt| opt.include?('features') }
+ args.push(DEFAULT_TEST_PATH_ARGS)
end
- Runtime::Scenario.define(:large_setup?, args.flatten.include?('can_use_large_setup'))
-
- if Runtime::Scenario.attributes[:parallel]
+ if Runtime::Env.knapsack?
+ KnapsackRunner.run(args.flatten) { |status| abort if status.nonzero? }
+ elsif Runtime::Scenario.attributes[:parallel]
ParallelRunner.run(args.flatten)
elsif Runtime::Scenario.attributes[:loop]
LoopRunner.run(args.flatten)
elsif Runtime::Scenario.attributes[:count_examples_only]
- args.unshift('--dry-run')
- out = StringIO.new
+ count_examples_only(args)
+ elsif Runtime::Scenario.attributes[:test_metadata_only]
+ test_metadata_only(args)
+ else
+ RSpec::Core::Runner.run(args.flatten, *DEFAULT_STD_ARGS).tap { |status| abort if status.nonzero? }
+ end
+ end
- RSpec::Core::Runner.run(args.flatten, $stderr, out).tap do |status|
- abort if status.nonzero?
- end
+ private
- begin
- total_examples = out.string.match(/(\d+) examples?,/)[1]
- rescue StandardError
- raise RegexMismatchError, 'Rspec output did not match regex'
- end
+ def count_examples_only(args)
+ args.unshift('--dry-run')
+ out = StringIO.new
- filename = build_filename
+ RSpec::Core::Runner.run(args.flatten, $stderr, out).tap do |status|
+ abort if status.nonzero?
+ end
- File.open(filename, 'w') { |f| f.write(total_examples) } if total_examples.to_i > 0
+ begin
+ total_examples = out.string.match(/(\d+) examples?,/)[1]
+ rescue StandardError
+ raise RegexMismatchError, 'Rspec output did not match regex'
+ end
- $stdout.puts "Total examples in #{Runtime::Scenario.klass}: #{total_examples}#{total_examples.to_i > 0 ? ". Saved to file: #{filename}" : ''}"
- elsif Runtime::Scenario.attributes[:test_metadata_only]
- args.unshift('--dry-run')
+ filename = build_filename
- output_file = Pathname.new(File.join(Runtime::Path.qa_root, 'tmp', 'test-metadata.json'))
+ File.open(filename, 'w') { |f| f.write(total_examples) } if total_examples.to_i > 0
- RSpec.configure do |config|
- config.add_formatter(QA::Support::JsonFormatter, output_file)
- config.fail_if_no_examples = true
- end
+ saved_file_msg = total_examples.to_i > 0 ? ". Saved to file: #{filename}" : ''
+ $stdout.puts "Total examples in #{Runtime::Scenario.klass}: #{total_examples}#{saved_file_msg}"
+ end
- RSpec::Core::Runner.run(args.flatten, $stderr, $stdout) do |status|
- abort if status.nonzero?
- end
+ def test_metadata_only(args)
+ args.unshift('--dry-run')
- $stdout.puts "Saved to file: #{output_file}"
- else
- RSpec::Core::Runner.run(args.flatten, *DEFAULT_STD_ARGS).tap do |status|
- abort if status.nonzero?
- end
+ output_file = Pathname.new(File.join(Runtime::Path.qa_root, 'tmp', 'test-metadata.json'))
+
+ RSpec.configure do |config|
+ config.add_formatter(QA::Support::JsonFormatter, output_file)
+ config.fail_if_no_examples = true
end
- end
- # rubocop:enable Metrics/AbcSize
- # rubocop:enable Metrics/CyclomaticComplexity
- private
+ RSpec::Core::Runner.run(args.flatten, $stderr, $stdout) do |status|
+ abort if status.nonzero?
+ end
+
+ $stdout.puts "Saved to file: #{output_file}"
+ end
def build_filename
filename = Runtime::Scenario.klass.split('::').last(3).join('_').downcase
tags = []
+ tag_opts = %w[--tag -t]
options.reduce do |before, after|
- tags << after if %w[--tag -t].include?(before)
+ tags << after if tag_opts.include?(before)
after
end
tags = tags.compact.join('_')
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
new file mode 100644
index 00000000000..b130fff0488
--- /dev/null
+++ b/qa/qa/specs/spec_helper.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require_relative '../../qa'
+
+require_relative 'qa_deprecation_toolkit_env'
+QaDeprecationToolkitEnv.configure!
+
+Knapsack::Adapters::RSpecAdapter.bind if QA::Runtime::Env.knapsack?
+
+QA::Support::GitlabAddress.define_gitlab_address_attribute!
+QA::Runtime::Browser.configure! unless QA::Runtime::Env.dry_run
+QA::Runtime::AllureReport.configure!
+QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
+
+Dir[::File.join(__dir__, "features/shared_examples/*.rb")].sort.each { |f| require f }
+Dir[::File.join(__dir__, "features/shared_contexts/*.rb")].sort.each { |f| require f }
+
+RSpec.configure do |config|
+ config.include QA::Support::Matchers::EventuallyMatcher
+ config.include QA::Support::Matchers::HaveMatcher
+
+ config.add_formatter QA::Support::Formatters::ContextFormatter
+ config.add_formatter QA::Support::Formatters::QuarantineFormatter
+ config.add_formatter QA::Support::Formatters::FeatureFlagFormatter
+ config.add_formatter QA::Support::Formatters::TestStatsFormatter if QA::Runtime::Env.export_metrics?
+
+ config.before(:suite) do |suite|
+ QA::Resource::ReusableCollection.register_resource_classes do |collection|
+ QA::Resource::ReusableProject.register(collection)
+ QA::Resource::ReusableGroup.register(collection)
+ end
+ end
+
+ config.prepend_before do |example|
+ QA::Runtime::Logger.info("Starting test: #{Rainbow(example.full_description).bright}")
+ QA::Runtime::Example.current = example
+
+ # Reset fabrication counters tracked in resource base
+ Thread.current[:api_fabrication] = 0
+ Thread.current[:browser_ui_fabrication] = 0
+ end
+
+ config.after do
+ # If a .netrc file was created during the test, delete it so that subsequent tests don't try to use the same logins
+ QA::Git::Repository.new.delete_netrc
+ end
+
+ # Add fabrication time to spec metadata
+ config.append_after do |example|
+ example.metadata[:api_fabrication] = Thread.current[:api_fabrication]
+ example.metadata[:browser_ui_fabrication] = Thread.current[:browser_ui_fabrication]
+ end
+
+ config.after(:context) do
+ if !QA::Runtime::Browser.blank_page? && QA::Page::Main::Menu.perform(&:signed_in?)
+ QA::Page::Main::Menu.perform(&:sign_out)
+ raise(
+ <<~ERROR
+ The test left the browser signed in.
+
+ Usually, Capybara prevents this from happening but some things can
+ interfere. For example, if it has an `after(:context)` block that logs
+ in, the browser will stay logged in and this will cause the next test
+ to fail.
+
+ Please make sure the test does not leave the browser signed in.
+ ERROR
+ )
+ end
+ end
+
+ config.after(:suite) do |suite|
+ # Write all test created resources to JSON file
+ QA::Tools::TestResourceDataProcessor.write_to_file(suite.reporter.failed_examples.any?)
+
+ # If requested, confirm that resources were used appropriately (e.g., not left with changes that interfere with
+ # further reuse)
+ QA::Resource::ReusableCollection.validate_resource_reuse if QA::Runtime::Env.validate_resource_reuse?
+
+ # If any tests failed, leave the resources behind to help troubleshoot, otherwise remove them.
+ # Do not remove the shared resource on live environments
+ begin
+ next if suite.reporter.failed_examples.present?
+ next unless QA::Runtime::Scenario.attributes.include?(:gitlab_address)
+ next if QA::Runtime::Env.running_on_dot_com?
+
+ QA::Resource::ReusableCollection.remove_all_via_api!
+ rescue QA::Resource::Errors::InternalServerError => e
+ # Temporarily prevent this error from failing jobs while the cause is investigated
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/354387
+ QA::Runtime::Logger.debug(e.message)
+ end
+ end
+
+ config.append_after(:suite) do
+ QA::Support::KnapsackReport.move_regenerated_report if QA::Runtime::Env.knapsack?
+ end
+
+ config.expect_with :rspec do |expectations|
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ config.mock_with :rspec do |mocks|
+ mocks.verify_partial_doubles = true
+ end
+
+ config.shared_context_metadata_behavior = :apply_to_host_groups
+ config.disable_monkey_patching!
+ config.expose_dsl_globally = true
+ config.profile_examples = 10
+ config.order = :random
+ Kernel.srand config.seed
+
+ # This option allows to use shorthand aliases for adding :focus metadata - fit, fdescribe and fcontext
+ config.filter_run_when_matching :focus
+
+ if ENV['CI'] && !QA::Runtime::Env.disable_rspec_retry?
+ # show retry status in spec process
+ config.verbose_retry = true
+
+ # show exception that triggers a retry if verbose_retry is set to true
+ config.display_try_failure_messages = true
+
+ non_quarantine_retries = QA::Runtime::Env.ci_project_name =~ /staging|canary|production/ ? 3 : 2
+ config.around do |example|
+ quarantine = example.metadata[:quarantine]
+ different_quarantine_context = QA::Specs::Helpers::Quarantine.quarantined_different_context?(quarantine)
+ focused_quarantine = QA::Specs::Helpers::Quarantine.filters.key?(:quarantine)
+
+ # Do not disable retry when spec is quarantined but on different environment
+ next example.run_with_retry(retry: non_quarantine_retries) if different_quarantine_context && !focused_quarantine
+
+ example.run_with_retry(retry: quarantine ? 1 : non_quarantine_retries)
+ end
+ end
+end
diff --git a/qa/qa/support/formatters/allure_metadata_formatter.rb b/qa/qa/support/formatters/allure_metadata_formatter.rb
index 2ed4ee2066a..d1baf87799a 100644
--- a/qa/qa/support/formatters/allure_metadata_formatter.rb
+++ b/qa/qa/support/formatters/allure_metadata_formatter.rb
@@ -20,11 +20,11 @@ module QA
def start(_start_notification)
return unless merge_request_iid # on main runs allure native history has pass rate already
- save_failures
- log(:debug, "Fetched #{failures.length} flaky testcases!")
+ save_flaky_specs
+ log(:debug, "Fetched #{flaky_specs.length} flaky testcases!")
rescue StandardError => e
log(:error, "Failed to fetch flaky spec data for report: #{e}")
- @failures = {}
+ @flaky_specs = {}
end
# Finished example
@@ -53,6 +53,8 @@ module QA
return unless issue_link
return example.issue('Quarantine issue', issue_link) if issue_link.is_a?(String)
return issue_link.each { |link| example.issue('Quarantine issue', link) } if issue_link.is_a?(Array)
+ rescue StandardError => e
+ log(:error, "Failed to add quarantine issue linkt for example '#{example.description}', error: #{e}")
end
# Add failure issues link
@@ -65,6 +67,8 @@ module QA
'Failure issues',
"https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{spec_file}"
)
+ rescue StandardError => e
+ log(:error, "Failed to add failure issue link for example '#{example.description}', error: #{e}")
end
# Add ci job link
@@ -75,6 +79,8 @@ module QA
return unless Runtime::Env.running_in_ci?
example.add_link(name: "Job(#{Runtime::Env.ci_job_name})", url: Runtime::Env.ci_job_url)
+ rescue StandardError => e
+ log(:error, "Failed to add failure issue link for example '#{example.description}', error: #{e}")
end
# Mark test as flaky
@@ -82,21 +88,22 @@ module QA
# @param [RSpec::Core::Example] example
# @return [void]
def set_flaky_status(example)
- return unless merge_request_iid
- return unless example.execution_result.status == :failed && failures.key?(example.metadata[:testcase])
+ return unless merge_request_iid && flaky_specs.key?(example.metadata[:testcase])
example.set_flaky
- example.parameter("pass_rate", "#{failures[example.metadata[:testcase]].round(1)}%")
- log(:debug, "Setting spec as flaky due to present failures in last 14 days!")
+ example.parameter("pass_rate", "#{flaky_specs[example.metadata[:testcase]].round(1)}%")
+ log(:debug, "Setting spec as flaky because it's pass rate is below 98%")
+ rescue StandardError => e
+ log(:error, "Failed to add spec pass rate data for example '#{example.description}', error: #{e}")
end
- # Failed spec testcases
+ # Flaky specs with pass rate below 98%
#
# @return [Array]
- def failures
- @failures ||= influx_data.lazy.each_with_object({}) do |data, result|
- # TODO: replace with mr_iid once stats are populated
- records = data.records.reject { |r| r.values["_value"] == env("CI_PIPELINE_ID") }
+ def flaky_specs
+ @flaky_specs ||= influx_data.lazy.each_with_object({}) do |data, result|
+ # Do not consider failures in same merge request
+ records = data.records.reject { |r| r.values["_value"] == merge_request_iid }
runs = records.count
failed = records.count { |r| r.values["status"] == "failed" }
@@ -107,7 +114,7 @@ module QA
end.compact
end
- alias_method :save_failures, :failures
+ alias_method :save_flaky_specs, :flaky_specs
# Records of previous failures for runs of same type
#
@@ -122,7 +129,7 @@ module QA
|> filter(fn: (r) => r.run_type == "#{run_type}" and
r.status != "pending" and
r.quarantined == "false" and
- r._field == "pipeline_id"
+ r._field == "merge_request_iid"
)
|> group(columns: ["testcase"])
QUERY
diff --git a/qa/qa/support/gitlab_address.rb b/qa/qa/support/gitlab_address.rb
new file mode 100644
index 00000000000..d978bb2eee5
--- /dev/null
+++ b/qa/qa/support/gitlab_address.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ class GitlabAddress
+ class << self
+ # Define gitlab address
+ #
+ # @param [String] address
+ # @return [void]
+ def define_gitlab_address_attribute!(address = Runtime::Env.gitlab_url)
+ return if initialized?
+
+ validate_address(address)
+
+ Runtime::Scenario.define(:gitlab_address, address)
+ # Define the "About" page as an `about` subdomain.
+ # @example
+ # Given *gitlab_address* = 'https://gitlab.com/' #=> https://about.gitlab.com/
+ # Given *gitlab_address* = 'https://staging.gitlab.com/' #=> https://about.staging.gitlab.com/
+ # Given *gitlab_address* = 'http://gitlab-abc123.test/' #=> http://about.gitlab-abc123.test/
+ Runtime::Scenario.define(:about_address, URI(address).tap { |uri| uri.host = "about.#{uri.host}" }.to_s)
+
+ @initialized = true
+ end
+
+ private
+
+ # Gitlab address already set up
+ #
+ # @return [Boolean]
+ def initialized?
+ @initialized
+ end
+
+ # Validate if address is a valid url
+ #
+ # @param [String] address
+ # @return [void]
+ def validate_address(address)
+ Runtime::Address.valid?(address) || raise(
+ ::ArgumentError, "Configured gitlab address is not a valid url: #{address}"
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/knapsack_report.rb b/qa/qa/support/knapsack_report.rb
index e50c4fe63d2..998802fe8b7 100644
--- a/qa/qa/tools/knapsack_report.rb
+++ b/qa/qa/support/knapsack_report.rb
@@ -3,7 +3,7 @@
require "fog/google"
module QA
- module Tools
+ module Support
class KnapsackReport
extend SingleForwardable
@@ -20,11 +20,10 @@ module QA
#
# @return [void]
def configure!
- ENV["KNAPSACK_TEST_FILE_PATTERN"] ||= "qa/specs/features/**/*_spec.rb"
- ENV["KNAPSACK_REPORT_PATH"] = report_path
-
- Knapsack.logger = QA::Runtime::Logger.logger
+ return unless QA::Runtime::Env.knapsack?
+ setup_logger!
+ setup_environment!
download_report
end
@@ -33,6 +32,8 @@ module QA
# @return [void]
def download_report
logger.debug("Downloading latest knapsack report for '#{report_name}' to '#{report_path}'")
+ return logger.debug("Report already exists, skipping!") if File.exist?(report_path)
+
file = client.get_object(BUCKET, report_file)
File.write(report_path, file[:body])
rescue StandardError => e
@@ -89,11 +90,27 @@ module QA
private
+ # Setup knapsack logger
+ #
+ # @return [void]
+ def setup_logger!
+ Knapsack.logger = QA::Runtime::Logger.logger
+ end
+
+ # Set knapsack environment variables
+ #
+ # @return [void]
+ def setup_environment!
+ ENV["KNAPSACK_TEST_FILE_PATTERN"] ||= "qa/specs/features/**/*_spec.rb"
+ ENV["KNAPSACK_TEST_DIR"] = "qa/specs"
+ ENV["KNAPSACK_REPORT_PATH"] = report_path
+ end
+
# Logger instance
#
# @return [Logger]
def logger
- @logger ||= Logger.new($stdout)
+ @logger ||= Knapsack.logger
end
# GCS client
@@ -131,7 +148,7 @@ module QA
#
# @return [String]
def report_name
- @report_name ||= ENV["CI_JOB_NAME"].split(" ").first.tr(":", "-")
+ @report_name ||= ENV["QA_KNAPSACK_REPORT_NAME"] || ENV["CI_JOB_NAME"].split(" ").first.tr(":", "-")
end
# GCS credentials json
diff --git a/qa/qa/support/matchers/have_matcher.rb b/qa/qa/support/matchers/have_matcher.rb
index b96566a9e5d..734a8890536 100644
--- a/qa/qa/support/matchers/have_matcher.rb
+++ b/qa/qa/support/matchers/have_matcher.rb
@@ -24,6 +24,7 @@ module QA
snippet_description
tag
label
+ variable
].each do |predicate|
RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs|
match do |page_object|
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index b402639b843..bc5ee645965 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -82,6 +82,12 @@ module QA
super
end
+ def click_via_capybara(method, locator)
+ log("clicking via capybara using '#{method}(#{locator})'")
+
+ super
+ end
+
def fill_element(name, content)
masked_content = name.to_s.match?(/token|key|password/) ? '*****' : content
diff --git a/qa/qa/tools/delete_test_snippets.rb b/qa/qa/tools/delete_test_snippets.rb
new file mode 100644
index 00000000000..5da962b14f3
--- /dev/null
+++ b/qa/qa/tools/delete_test_snippets.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+# This script deletes personal snippets for a specific user
+# - Specify `delete_before` to delete only snippets that were created before the given date (default: yesterday)
+# - If `dry_run` is true the script will list snippets to be deleted, but it won't delete them
+#
+# Required environment variables: GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
+# - GITLAB_QA_ACCESS_TOKEN should have API access and belong to the user whose snippets will be deleted
+
+module QA
+ module Tools
+ class DeleteTestSnippets
+ include Support::API
+
+ ITEMS_PER_PAGE = '100'
+
+ def initialize(delete_before: (Date.today - 1).to_s, dry_run: false)
+ raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
+ raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN" unless ENV['GITLAB_QA_ACCESS_TOKEN']
+
+ @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'],
+ personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN'])
+ @delete_before = Date.parse(delete_before)
+ @dry_run = dry_run
+ end
+
+ def run
+ $stdout.puts 'Running...'
+
+ response = head Runtime::API::Request.new(@api_client, "/snippets", per_page: ITEMS_PER_PAGE).url
+ total_pages = response.headers[:x_total_pages]
+
+ test_snippet_ids = fetch_snippet_ids(total_pages)
+ $stdout.puts "Number of test snippets to be deleted: #{test_snippet_ids.length}"
+
+ return if dry_run?
+
+ delete_snippets(test_snippet_ids) unless test_snippet_ids.empty?
+ $stdout.puts "\nDone"
+ end
+
+ private
+
+ attr_reader :dry_run
+ alias_method :dry_run?, :dry_run
+
+ def delete_snippets(snippet_ids)
+ $stdout.puts "Deleting #{snippet_ids.length} snippet(s)..."
+ snippet_ids.each do |snippet_id|
+ delete_response = delete Runtime::API::Request.new(@api_client, "/snippets/#{snippet_id}").url
+ dot_or_f = delete_response.code == 204 ? "\e[32m.\e[0m" : "\e[31mF\e[0m"
+ print dot_or_f
+ end
+ end
+
+ def fetch_snippet_ids(pages)
+ snippet_ids = []
+
+ pages.to_i.times do |page_no|
+ get_snippet_response = get Runtime::API::Request.new(@api_client, "/snippets",
+ page: (page_no + 1).to_s, per_page: ITEMS_PER_PAGE).url
+ snippets = JSON.parse(get_snippet_response.body).select do |snippet|
+ to_delete = Date.parse(snippet['created_at']) < @delete_before
+
+ if dry_run?
+ puts "Snippet title: #{snippet['title']}\tcreated_at: #{snippet['created_at']}\tdelete? #{to_delete}"
+ end
+
+ to_delete
+ end
+ snippet_ids.concat(snippets.map { |snippet| snippet['id'] })
+ end
+
+ snippet_ids.uniq
+ end
+ end
+ end
+end
diff --git a/qa/spec/README.md b/qa/spec/README.md
new file mode 100644
index 00000000000..b1fc38fb55d
--- /dev/null
+++ b/qa/spec/README.md
@@ -0,0 +1,7 @@
+# QA framework unit tests
+
+To run framework unit tests, following command can be used:
+
+```shell
+bundle exec rspec -O .rspec_internal
+```
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 6b100f9dc16..a6a49f5907a 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -206,19 +206,19 @@ RSpec.describe QA::Git::Repository do
it_behaves_like 'command with retries' do
let(:command) { "git ls-remote #{repo_uri_with_credentials}" }
- let(:result_output) { +'packet: git< version 2' }
+ let(:result_output) { +'packet: ls-remote< version 2' }
let(:command_return) { '2' }
let(:extra_env_vars) { ["GIT_TRACE_PACKET=1"] }
end
it "reports the detected version" do
- expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: git< version 2"))
+ expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: ls-remote< version 2"))
expect(call_method).to eq('2')
end
it 'reports unknown if version is unknown' do
- expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: git< version -1"))
+ expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: ls-remote< version -1"))
expect(call_method).to eq('unknown')
end
diff --git a/qa/spec/scenario/template_spec.rb b/qa/spec/scenario/template_spec.rb
index 9800f92b306..56521cc13bc 100644
--- a/qa/spec/scenario/template_spec.rb
+++ b/qa/spec/scenario/template_spec.rb
@@ -1,35 +1,49 @@
# frozen_string_literal: true
RSpec.describe QA::Scenario::Template do
- let(:feature) { spy('Runtime::Feature') }
- let(:release) { spy('Runtime::Release') }
+ let(:release) { spy('QA::Runtime::Release') } # rubocop:disable RSpec/VerifiedDoubles
+ let(:feature) { class_spy('QA::Runtime::Feature') }
+ let(:scenario) { class_spy('QA::Runtime::Scenario') }
+ let(:runner) { class_spy('QA::Specs::Runner') }
+
let(:gitlab_address) { 'https://gitlab.com/' }
+ let(:gitlab_address_from_env) { 'https://staging.gitlab.com/' }
before do
stub_const('QA::Runtime::Release', release)
stub_const('QA::Runtime::Feature', feature)
- allow(QA::Specs::Runner).to receive(:perform)
- allow(QA::Runtime::Address).to receive(:valid?).and_return(true)
+ stub_const('QA::Runtime::Scenario', scenario)
+ stub_const('QA::Specs::Runner', runner)
+
+ allow(QA::Runtime::Env).to receive(:knapsack?).and_return(false)
+ allow(QA::Runtime::Env).to receive(:gitlab_url).and_return(gitlab_address_from_env)
+
+ allow(QA::Runtime::Browser).to receive(:configure!)
+
+ allow(scenario).to receive(:attributes).and_return({ gitlab_address: gitlab_address })
+ allow(scenario).to receive(:define)
+
+ QA::Support::GitlabAddress.instance_variable_set(:@initialized, false)
end
it 'allows a feature to be enabled' do
subject.perform({ gitlab_address: gitlab_address, enable_feature: 'a-feature' })
expect(feature).to have_received(:enable).with('a-feature')
+ expect(feature).to have_received(:disable).with('a-feature')
end
it 'allows a feature to be disabled' do
- allow(QA::Runtime::Feature).to receive(:enabled?)
- .with('another-feature').and_return(true)
+ allow(QA::Runtime::Feature).to receive(:enabled?).with('another-feature').and_return(true)
subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' })
expect(feature).to have_received(:disable).with('another-feature')
+ expect(feature).to have_received(:enable).with('another-feature')
end
it 'does not disable a feature if already disabled' do
- allow(QA::Runtime::Feature).to receive(:enabled?)
- .with('another-feature').and_return(false)
+ allow(QA::Runtime::Feature).to receive(:enabled?).with('another-feature').and_return(false)
subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' })
@@ -39,7 +53,8 @@ RSpec.describe QA::Scenario::Template do
it 'ensures an enabled feature is disabled afterwards' do
allow(QA::Specs::Runner).to receive(:perform).and_raise('failed test')
- expect { subject.perform({ gitlab_address: gitlab_address, enable_feature: 'a-feature' }) }.to raise_error('failed test')
+ expect { subject.perform({ gitlab_address: gitlab_address, enable_feature: 'a-feature' }) }
+ .to raise_error('failed test')
expect(feature).to have_received(:enable).with('a-feature')
expect(feature).to have_received(:disable).with('a-feature')
@@ -47,11 +62,10 @@ RSpec.describe QA::Scenario::Template do
it 'ensures a disabled feature is enabled afterwards' do
allow(QA::Specs::Runner).to receive(:perform).and_raise('failed test')
+ allow(QA::Runtime::Feature).to receive(:enabled?).with('another-feature').and_return(true)
- allow(QA::Runtime::Feature).to receive(:enabled?)
- .with('another-feature').and_return(true)
-
- expect { subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' }) }.to raise_error('failed test')
+ expect { subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' }) }
+ .to raise_error('failed test')
expect(feature).to have_received(:disable).with('another-feature')
expect(feature).to have_received(:enable).with('another-feature')
@@ -59,25 +73,35 @@ RSpec.describe QA::Scenario::Template do
it 'ensures a disabled feature is not enabled afterwards if it was disabled earlier' do
allow(QA::Specs::Runner).to receive(:perform).and_raise('failed test')
+ allow(QA::Runtime::Feature).to receive(:enabled?).with('another-feature').and_return(false)
- allow(QA::Runtime::Feature).to receive(:enabled?)
- .with('another-feature').and_return(false)
-
- expect { subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' }) }.to raise_error('failed test')
+ expect { subject.perform({ gitlab_address: gitlab_address, disable_feature: 'another-feature' }) }
+ .to raise_error('failed test')
expect(feature).not_to have_received(:disable).with('another-feature')
expect(feature).not_to have_received(:enable).with('another-feature')
end
- it 'defines an about address by default' do
- subject.perform( { gitlab_address: gitlab_address })
+ it 'defines gitlab address from positional argument' do
+ allow(scenario).to receive(:attributes).and_return({})
+
+ subject.perform({}, gitlab_address)
+
+ expect(scenario).to have_received(:define).with(:gitlab_address, gitlab_address)
+ expect(scenario).to have_received(:define).with(:about_address, 'https://about.gitlab.com/')
+ end
- expect(QA::Runtime::Scenario.gitlab_address).to eq(gitlab_address)
- expect(QA::Runtime::Scenario.about_address).to eq('https://about.gitlab.com/')
+ it "defaults to gitlab address from env" do
+ allow(scenario).to receive(:attributes).and_return({})
+
+ subject.perform({})
+
+ expect(scenario).to have_received(:define).with(:gitlab_address, gitlab_address_from_env)
+ end
- subject.perform({ gitlab_address: 'http://gitlab-abc.test/' })
+ it 'defines klass attribute' do
+ subject.perform({ gitlab_address: gitlab_address })
- expect(QA::Runtime::Scenario.gitlab_address).to eq('http://gitlab-abc.test/')
- expect(QA::Runtime::Scenario.about_address).to eq('http://about.gitlab-abc.test/')
+ expect(scenario).to have_received(:define).with(:klass, 'QA::Scenario::Template')
end
end
diff --git a/qa/spec/scenario/test/integration/mattermost_spec.rb b/qa/spec/scenario/test/integration/mattermost_spec.rb
index 9532ec35b95..4cdd0c0cb0e 100644
--- a/qa/spec/scenario/test/integration/mattermost_spec.rb
+++ b/qa/spec/scenario/test/integration/mattermost_spec.rb
@@ -3,24 +3,10 @@
RSpec.describe QA::Scenario::Test::Integration::Mattermost do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
- let(:args) { %w[gitlab_address mattermost_address] }
- let(:args) do
- {
- gitlab_address: 'http://gitlab_address',
- mattermost_address: 'http://mattermost_address'
- }
- end
-
+ let(:args) { { gitlab_address: 'http://gitlab_address' } }
let(:named_options) { %w[--address http://gitlab_address --mattermost-address http://mattermost_address] }
let(:tags) { [:mattermost] }
- let(:options) { ['path1']}
-
- it 'requires a GitHub access token' do
- subject.perform(args)
-
- expect(attributes).to have_received(:define)
- .with(:mattermost_address, 'http://mattermost_address')
- end
+ let(:options) { ['path1'] }
end
end
end
diff --git a/qa/spec/specs/scenario_shared_examples.rb b/qa/spec/scenario_shared_examples.rb
index 7d806d50d21..944e309c4cb 100644
--- a/qa/spec/specs/scenario_shared_examples.rb
+++ b/qa/spec/scenario_shared_examples.rb
@@ -2,7 +2,7 @@
module QA
RSpec.shared_examples 'a QA scenario class' do
- let(:attributes) { class_spy('Runtime::Scenario') }
+ let(:scenario) { class_spy('Runtime::Scenario') }
let(:runner) { class_spy('Specs::Runner') }
let(:release) { class_spy('Runtime::Release') }
let(:feature) { class_spy('Runtime::Feature') }
@@ -15,24 +15,19 @@ module QA
before do
stub_const('QA::Specs::Runner', runner)
stub_const('QA::Runtime::Release', release)
- stub_const('QA::Runtime::Scenario', attributes)
+ stub_const('QA::Runtime::Scenario', scenario)
stub_const('QA::Runtime::Feature', feature)
- allow(attributes).to receive(:gitlab_address).and_return(args[:gitlab_address])
+ allow(QA::Runtime::Browser).to receive(:configure!)
+
+ allow(scenario).to receive(:attributes).and_return(args)
allow(runner).to receive(:perform).and_yield(runner)
- allow(QA::Runtime::Address).to receive(:valid?).and_return(true)
end
it 'responds to perform' do
expect(subject).to respond_to(:perform)
end
- it 'sets an address of the subject' do
- subject.perform(args)
-
- expect(attributes).to have_received(:define).with(:gitlab_address, 'http://gitlab_address').at_least(:once)
- end
-
it 'performs before hooks only once' do
subject.perform(args)
@@ -58,7 +53,7 @@ module QA
described_class.launch!(named_options)
args do |k, v|
- expect(attributes).to have_received(:define).with(k, v)
+ expect(scenario).to have_received(:define).with(k, v)
end
end
@@ -67,7 +62,7 @@ module QA
end
it 'passes on options after --' do
- expect(described_class).to receive(:perform).with(attributes, *%w[--tag quarantine])
+ expect(described_class).to receive(:perform).with(args, *%w[--tag quarantine])
described_class.launch!(named_options.push(*%w[-- --tag quarantine]))
end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index b81c41bb79c..95970800a4e 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -2,134 +2,4 @@
require_relative '../qa'
-require_relative 'qa_deprecation_toolkit_env'
-QaDeprecationToolkitEnv.configure!
-
-Knapsack::Adapters::RSpecAdapter.bind if QA::Runtime::Env.knapsack?
-
-QA::Runtime::Browser.configure! unless QA::Runtime::Env.dry_run
-QA::Runtime::AllureReport.configure!
-QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
-
-Dir[::File.join(__dir__, "support/shared_examples/*.rb")].sort.each { |f| require f }
-Dir[::File.join(__dir__, "support/shared_contexts/*.rb")].sort.each { |f| require f }
-
-RSpec.configure do |config|
- config.include QA::Support::Matchers::EventuallyMatcher
- config.include QA::Support::Matchers::HaveMatcher
-
- config.add_formatter QA::Support::Formatters::ContextFormatter
- config.add_formatter QA::Support::Formatters::QuarantineFormatter
- config.add_formatter QA::Support::Formatters::FeatureFlagFormatter
- config.add_formatter QA::Support::Formatters::TestStatsFormatter if QA::Runtime::Env.export_metrics?
-
- config.before(:suite) do |suite|
- QA::Resource::ReusableCollection.register_resource_classes do |collection|
- QA::Resource::ReusableProject.register(collection)
- QA::Resource::ReusableGroup.register(collection)
- end
- end
-
- config.prepend_before do |example|
- QA::Runtime::Logger.info("Starting test: #{Rainbow(example.full_description).bright}")
- QA::Runtime::Example.current = example
-
- # Reset fabrication counters tracked in resource base
- Thread.current[:api_fabrication] = 0
- Thread.current[:browser_ui_fabrication] = 0
- end
-
- config.after do
- # If a .netrc file was created during the test, delete it so that subsequent tests don't try to use the same logins
- QA::Git::Repository.new.delete_netrc
- end
-
- # Add fabrication time to spec metadata
- config.append_after do |example|
- example.metadata[:api_fabrication] = Thread.current[:api_fabrication]
- example.metadata[:browser_ui_fabrication] = Thread.current[:browser_ui_fabrication]
- end
-
- config.after(:context) do
- if !QA::Runtime::Browser.blank_page? && QA::Page::Main::Menu.perform(&:signed_in?)
- QA::Page::Main::Menu.perform(&:sign_out)
- raise(
- <<~ERROR
- The test left the browser signed in.
-
- Usually, Capybara prevents this from happening but some things can
- interfere. For example, if it has an `after(:context)` block that logs
- in, the browser will stay logged in and this will cause the next test
- to fail.
-
- Please make sure the test does not leave the browser signed in.
- ERROR
- )
- end
- end
-
- config.after(:suite) do |suite|
- # Write all test created resources to JSON file
- QA::Tools::TestResourceDataProcessor.write_to_file(suite.reporter.failed_examples.any?)
-
- # If requested, confirm that resources were used appropriately (e.g., not left with changes that interfere with
- # further reuse)
- QA::Resource::ReusableCollection.validate_resource_reuse if QA::Runtime::Env.validate_resource_reuse?
-
- # If any tests failed, leave the resources behind to help troubleshoot, otherwise remove them.
- # Do not remove the shared resource on live environments
- begin
- next if suite.reporter.failed_examples.present?
- next unless QA::Runtime::Scenario.attributes.include?(:gitlab_address)
- next if QA::Runtime::Env.running_on_dot_com?
-
- QA::Resource::ReusableCollection.remove_all_via_api!
- rescue QA::Resource::Errors::InternalServerError => e
- # Temporarily prevent this error from failing jobs while the cause is investigated
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/354387
- QA::Runtime::Logger.debug(e.message)
- end
- end
-
- config.append_after(:suite) do
- QA::Tools::KnapsackReport.move_regenerated_report if QA::Runtime::Env.knapsack?
- end
-
- config.expect_with :rspec do |expectations|
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
- end
-
- config.mock_with :rspec do |mocks|
- mocks.verify_partial_doubles = true
- end
-
- config.shared_context_metadata_behavior = :apply_to_host_groups
- config.disable_monkey_patching!
- config.expose_dsl_globally = true
- config.profile_examples = 10
- config.order = :random
- Kernel.srand config.seed
-
- # show retry status in spec process
- config.verbose_retry = true
-
- # show exception that triggers a retry if verbose_retry is set to true
- config.display_try_failure_messages = true
-
- # This option allows to use shorthand aliases for adding :focus metadata - fit, fdescribe and fcontext
- config.filter_run_when_matching :focus
-
- if ENV['CI'] && !QA::Runtime::Env.disable_rspec_retry?
- non_quarantine_retries = QA::Runtime::Env.ci_project_name =~ /staging|canary|production/ ? 3 : 2
- config.around do |example|
- quarantine = example.metadata[:quarantine]
- different_quarantine_context = QA::Specs::Helpers::Quarantine.quarantined_different_context?(quarantine)
- focused_quarantine = QA::Specs::Helpers::Quarantine.filters.key?(:quarantine)
-
- # Do not disable retry when spec is quarantined but on different environment
- next example.run_with_retry(retry: non_quarantine_retries) if different_quarantine_context && !focused_quarantine
-
- example.run_with_retry(retry: quarantine ? 1 : non_quarantine_retries)
- end
- end
-end
+require_relative 'scenario_shared_examples'
diff --git a/qa/spec/specs/spec_helper.rb b/qa/spec/specs/spec_helper.rb
deleted file mode 100644
index e4514c6c64f..00000000000
--- a/qa/spec/specs/spec_helper.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-require_relative '../../qa'
-
-require_relative 'scenario_shared_examples'
diff --git a/qa/tasks/knapsack.rake b/qa/tasks/knapsack.rake
index ce4a1679bd1..cfc11d0ba24 100644
--- a/qa/tasks/knapsack.rake
+++ b/qa/tasks/knapsack.rake
@@ -1,20 +1,34 @@
# frozen_string_literal: true
-# rubocop:disable Rails/RakeEnvironment
+# rubocop:disable Rails/RakeEnvironment
namespace :knapsack do
+ desc "Run tests with knapsack runner"
+ task :rspec, [:rspec_args] do |_, args|
+ rspec_args = args[:rspec_args]&.split(' ') || []
+
+ unless QA::Runtime::Env.knapsack?
+ QA::Runtime::Logger.info("This environment is not compatible with parallel knapsack execution!")
+ QA::Runtime::Logger.info("Falling back to standard execution")
+
+ exit RSpec::Core::Runner.run([*rspec_args, "qa/specs/features"])
+ end
+
+ exit QA::Specs::KnapsackRunner.run(rspec_args)
+ end
+
desc "Download latest knapsack report"
task :download do
- QA::Tools::KnapsackReport.download
+ QA::Support::KnapsackReport.download_report
end
desc "Merge and upload knapsack report"
task :upload, [:glob] do |_task, args|
- QA::Tools::KnapsackReport.upload_report(args[:glob])
+ QA::Support::KnapsackReport.upload_report(args[:glob])
end
desc "Report long running spec files"
task :notify_long_running_specs do
- QA::Tools::LongRunningSpecReporter.execute
+ QA::Support::LongRunningSpecReporter.execute
end
end
# rubocop:enable Rails/RakeEnvironment
diff --git a/qa/tasks/vulnerabilities.rake b/qa/tasks/vulnerabilities.rake
new file mode 100644
index 00000000000..79d6b8683e0
--- /dev/null
+++ b/qa/tasks/vulnerabilities.rake
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+# rubocop:disable Rails/RakeEnvironment
+
+# How to run this rake task?
+# GITLAB_QA_ACCESS_TOKEN=<access_token> GITLAB_URL="<Gitlab address>" bundle exec rake
+# vulnerabilities:setup\[<Project_id>,<Vulnerability_count>\] --trace
+
+namespace :vulnerabilities do
+ desc "Set up test data for vulnerability report"
+ task :setup, [:project_id, :vulnerability_count] do |t, args|
+ QA::Runtime::Browser.configure!
+ QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
+
+ if ENV['GITLAB_URL'].nil?
+ puts 'ERROR: Exiting rake, Gitlab address not specified as GITLAB_URL environment variable'
+ exit 1
+ end
+
+ if ENV['GITLAB_QA_ACCESS_TOKEN'].nil?
+ puts 'ERROR: Exiting rake, API access token not provided as GITLAB_QA_ACCESS_TOKEN environment variable'
+ exit 1
+ end
+
+ QA::Runtime::Scenario.define(:gitlab_address, ENV['GITLAB_URL'])
+ vuln = QA::EE::Resource::VulnerabilityReport.new
+ vuln.create_vuln_report(args[:project_id], args[:vulnerability_count].to_i)
+ end
+end
+# rubocop:enable Rails/RakeEnvironment