summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rw-r--r--qa/Dockerfile10
-rw-r--r--qa/Gemfile16
-rw-r--r--qa/Gemfile.lock36
-rw-r--r--qa/gdk/Dockerfile50
-rw-r--r--qa/gdk/gdk.yml26
-rwxr-xr-xqa/gdk/launch40
-rw-r--r--qa/lib/gitlab/page/group/settings/usage_quotas.rb11
-rw-r--r--qa/lib/gitlab/page/trials/new.rb (renamed from qa/qa/page/trials/new.rb)2
-rw-r--r--qa/lib/gitlab/page/trials/select.rb (renamed from qa/qa/page/trials/select.rb)11
-rw-r--r--qa/lib/slack.rb9
-rw-r--r--qa/lib/slack/mixins/browser.rb11
-rw-r--r--qa/lib/slack/mixins/gitlab_app.rb52
-rw-r--r--qa/lib/slack/page/chat.rb68
-rw-r--r--qa/lib/slack/page/login.rb40
-rw-r--r--qa/lib/slack/page/oauth.rb9
-rw-r--r--qa/qa.rb5
-rw-r--r--qa/qa/fixtures/gpg/admin.ascbin0 -> 4825 bytes
-rw-r--r--qa/qa/fixtures/package_managers/helm/helm_install_package.yaml.erb3
-rw-r--r--qa/qa/fixtures/package_managers/helm/helm_upload_package.yaml.erb3
-rw-r--r--qa/qa/flow/alert_settings.rb102
-rw-r--r--qa/qa/flow/integrations/slack.rb54
-rw-r--r--qa/qa/flow/trial.rb39
-rw-r--r--qa/qa/flow/user_onboarding.rb1
-rw-r--r--qa/qa/page/component/blob_content.rb2
-rw-r--r--qa/qa/page/component/design_management.rb2
-rw-r--r--qa/qa/page/component/dropdown.rb53
-rw-r--r--qa/qa/page/component/invite_members_modal.rb90
-rw-r--r--qa/qa/page/component/members/invite_members_modal.rb92
-rw-r--r--qa/qa/page/component/members/members_filter.rb27
-rw-r--r--qa/qa/page/component/members/members_table.rb99
-rw-r--r--qa/qa/page/component/members/remove_group_modal.rb38
-rw-r--r--qa/qa/page/component/members/remove_member_modal.rb38
-rw-r--r--qa/qa/page/component/members_filter.rb25
-rw-r--r--qa/qa/page/component/select2.rb68
-rw-r--r--qa/qa/page/component/wiki_sidebar.rb5
-rw-r--r--qa/qa/page/dashboard/todos.rb40
-rw-r--r--qa/qa/page/group/members.rb59
-rw-r--r--qa/qa/page/group/menu.rb16
-rw-r--r--qa/qa/page/group/settings/general.rb2
-rw-r--r--qa/qa/page/group/show.rb5
-rw-r--r--qa/qa/page/main/login.rb46
-rw-r--r--qa/qa/page/merge_request/new.rb8
-rw-r--r--qa/qa/page/merge_request/show.rb17
-rw-r--r--qa/qa/page/profile/chat_names/new.rb13
-rw-r--r--qa/qa/page/project/import/github.rb8
-rw-r--r--qa/qa/page/project/members.rb45
-rw-r--r--qa/qa/page/project/monitor/alerts/index.rb2
-rw-r--r--qa/qa/page/project/monitor/incidents/index.rb5
-rw-r--r--qa/qa/page/project/secure/configuration_form.rb1
-rw-r--r--qa/qa/page/project/settings/alerts.rb13
-rw-r--r--qa/qa/page/project/settings/default_branch.rb11
-rw-r--r--qa/qa/page/project/settings/integrations.rb5
-rw-r--r--qa/qa/page/project/settings/main.rb1
-rw-r--r--qa/qa/page/project/settings/merge_request.rb4
-rw-r--r--qa/qa/page/registration/welcome.rb4
-rw-r--r--qa/qa/page/search/results.rb6
-rw-r--r--qa/qa/resource/api_fabricator.rb10
-rw-r--r--qa/qa/resource/design.rb58
-rw-r--r--qa/qa/resource/import_project.rb2
-rw-r--r--qa/qa/resource/members.rb10
-rw-r--r--qa/qa/resource/pipeline.rb14
-rw-r--r--qa/qa/resource/project.rb10
-rw-r--r--qa/qa/resource/project_web_hook.rb19
-rw-r--r--qa/qa/resource/runner_base.rb2
-rw-r--r--qa/qa/resource/user.rb6
-rw-r--r--qa/qa/runtime/application_settings.rb4
-rw-r--r--qa/qa/runtime/browser.rb49
-rw-r--r--qa/qa/runtime/env.rb21
-rw-r--r--qa/qa/runtime/fixtures.rb9
-rw-r--r--qa/qa/runtime/gpg.rb29
-rw-r--r--qa/qa/scenario/test/integration/gitaly_cluster.rb13
-rw-r--r--qa/qa/scenario/test/integration/integrations.rb (renamed from qa/qa/scenario/test/instance/integrations.rb)4
-rw-r--r--qa/qa/scenario/test/integration/jira.rb (renamed from qa/qa/scenario/test/instance/jira.rb)4
-rw-r--r--qa/qa/scenario/test/integration/mtls.rb13
-rw-r--r--qa/qa/service/cluster_provider/gcloud.rb2
-rw-r--r--qa/qa/service/docker_run/ldap.rb2
-rw-r--r--qa/qa/service/shellout.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb5
-rw-r--r--qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb41
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb14
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb12
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/group_member_access_request_spec.rb93
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb12
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb103
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb85
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb55
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb32
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb115
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb54
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb149
-rw-r--r--qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb23
-rw-r--r--qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb2
-rw-r--r--qa/qa/specs/knapsack_runner.rb2
-rw-r--r--qa/qa/specs/spec_helper.rb1
-rw-r--r--qa/qa/support/matchers/eventually_matcher.rb7
-rw-r--r--qa/qa/tools/ci/test_results.rb78
-rw-r--r--qa/spec/resource/api_fabricator_spec.rb12
-rw-r--r--qa/spec/runtime/script_extensions/interceptor_spec.rb2
-rw-r--r--qa/tasks/ci.rake10
-rw-r--r--qa/tasks/knapsack.rake2
121 files changed, 1835 insertions, 972 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 8e79d0a7bad..6efc8ac09fa 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -15,11 +15,11 @@ ENV WD_INSTALL_DIR=/usr/local/bin
##
# Install system libs
#
-RUN apt-get update; \
- apt-get install -y xvfb unzip; \
- apt-get -yq autoremove; \
- apt-get clean -yqq; \
- rm -rf /var/lib/apt/lists/*
+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
diff --git a/qa/Gemfile b/qa/Gemfile
index 53dc071b766..9e41b5ddeed 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -2,21 +2,21 @@
source 'https://rubygems.org'
-gem 'gitlab-qa', '~> 8', '>= 8.15.1', require: 'gitlab/qa'
-gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile
+gem 'gitlab-qa', '~> 9', require: 'gitlab/qa'
+gem 'activesupport', '~> 6.1.7.2' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.20.0'
gem 'capybara', '~> 3.38.0'
gem 'capybara-screenshot', '~> 1.0.26'
gem 'rake', '~> 13', '>= 13.0.6'
gem 'rspec', '~> 3.12'
-gem 'selenium-webdriver', '~> 4.7', '>= 4.7.1'
+gem 'selenium-webdriver', '~> 4.8'
gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default
gem 'rest-client', '~> 2.1.0'
gem 'rspec-retry', '~> 0.6.2', require: 'rspec/retry'
gem 'rspec_junit_formatter', '~> 0.6.0'
-gem 'faker', '~> 3.1'
+gem 'faker', '~> 3.1', '>= 3.1.1'
gem 'knapsack', '~> 4.0'
-gem 'parallel_tests', '~> 4.1'
+gem 'parallel_tests', '~> 4.2'
gem 'rotp', '~> 6.2.2'
gem 'parallel', '~> 1.22', '>= 1.22.1'
gem 'rainbow', '~> 3.1.1'
@@ -24,7 +24,7 @@ gem 'rspec-parameterized', '~> 1.0.0'
gem 'octokit', '~> 6.0.1'
gem "faraday-retry", "~> 2.0"
gem 'webdrivers', '~> 5.2'
-gem 'zeitwerk', '~> 2.6', '>= 2.6.6'
+gem 'zeitwerk', '~> 2.6', '>= 2.6.7'
gem 'influxdb-client', '~> 2.9'
gem 'terminal-table', '~> 3.0.2', require: false
gem 'slack-notifier', '~> 2.4', require: false
@@ -38,9 +38,9 @@ gem 'chemlab', '~> 0.10'
gem 'chemlab-library-www-gitlab-com', '~> 0.1', '>= 0.1.1'
# dependencies for jenkins client
-gem 'nokogiri', '~> 1.14'
+gem 'nokogiri', '~> 1.14', '>= 1.14.2'
-gem 'deprecation_toolkit', '~> 2.0.1', require: false
+gem 'deprecation_toolkit', '~> 2.0.3', require: false
group :development do
gem 'pry-byebug', '~> 3.10.1', platform: :mri
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 577b641685a..d544aa685a5 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
- activesupport (6.1.4.7)
+ activesupport (6.1.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -57,13 +57,13 @@ GEM
zeitwerk (>= 2.5, < 3)
debug_inspector (1.1.0)
declarative (0.0.20)
- deprecation_toolkit (2.0.1)
+ deprecation_toolkit (2.0.3)
activesupport (>= 5.2)
diff-lcs (1.3)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
excon (0.92.4)
- faker (3.1.0)
+ faker (3.1.1)
i18n (>= 1.8.11, < 2)
faraday (2.5.2)
faraday-net_http (>= 2.0, < 3.1)
@@ -102,7 +102,7 @@ GEM
gitlab (4.18.0)
httparty (~> 0.18)
terminal-table (>= 1.5.1)
- gitlab-qa (8.15.1)
+ gitlab-qa (9.0.0)
activesupport (~> 6.1)
gitlab (~> 4.18.0)
http (~> 5.0)
@@ -176,11 +176,11 @@ GEM
mime-types-data (3.2022.0105)
mini_mime (1.1.0)
mini_portile2 (2.8.1)
- minitest (5.16.3)
+ minitest (5.17.0)
multi_json (1.15.0)
multi_xml (0.6.0)
netrc (0.11.0)
- nokogiri (1.14.0)
+ nokogiri (1.14.2)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
octokit (6.0.1)
@@ -189,7 +189,7 @@ GEM
oj (3.13.23)
os (1.1.4)
parallel (1.22.1)
- parallel_tests (4.1.0)
+ parallel_tests (4.2.0)
parallel
parser (3.1.3.0)
ast (~> 2.4.1)
@@ -259,7 +259,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
- selenium-webdriver (4.7.1)
+ selenium-webdriver (4.8.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@@ -298,13 +298,13 @@ GEM
websocket (1.2.9)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.6.6)
+ zeitwerk (2.6.7)
PLATFORMS
ruby
DEPENDENCIES
- activesupport (~> 6.1.4.7)
+ activesupport (~> 6.1.7.2)
airborne (~> 0.3.7)
allure-rspec (~> 2.20.0)
capybara (~> 3.38.0)
@@ -312,18 +312,18 @@ DEPENDENCIES
chemlab (~> 0.10)
chemlab-library-www-gitlab-com (~> 0.1, >= 0.1.1)
confiner (~> 0.4)
- deprecation_toolkit (~> 2.0.1)
- faker (~> 3.1)
+ deprecation_toolkit (~> 2.0.3)
+ faker (~> 3.1, >= 3.1.1)
faraday-retry (~> 2.0)
fog-core (= 2.1.0)
fog-google (~> 1.19)
- gitlab-qa (~> 8, >= 8.15.1)
+ gitlab-qa (~> 9)
influxdb-client (~> 2.9)
knapsack (~> 4.0)
- nokogiri (~> 1.14)
+ nokogiri (~> 1.14, >= 1.14.2)
octokit (~> 6.0.1)
parallel (~> 1.22, >= 1.22.1)
- parallel_tests (~> 4.1)
+ parallel_tests (~> 4.2)
pry-byebug (~> 3.10.1)
rainbow (~> 3.1.1)
rake (~> 13, >= 13.0.6)
@@ -334,12 +334,12 @@ DEPENDENCIES
rspec-retry (~> 0.6.2)
rspec_junit_formatter (~> 0.6.0)
ruby-debug-ide (~> 0.7.3)
- selenium-webdriver (~> 4.7, >= 4.7.1)
+ selenium-webdriver (~> 4.8)
slack-notifier (~> 2.4)
terminal-table (~> 3.0.2)
warning (~> 1.3)
webdrivers (~> 5.2)
- zeitwerk (~> 2.6, >= 2.6.6)
+ zeitwerk (~> 2.6, >= 2.6.7)
BUNDLED WITH
- 2.3.26
+ 2.4.6
diff --git a/qa/gdk/Dockerfile b/qa/gdk/Dockerfile
new file mode 100644
index 00000000000..ed8f3f317eb
--- /dev/null
+++ b/qa/gdk/Dockerfile
@@ -0,0 +1,50 @@
+FROM registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-gdk-installed-gitlab-e2e:ml-create-image-for-gitlab-qa-tests
+
+ENV CHROME_DRIVER_VERSION="107.0.5304.62"
+ENV CHROME_VERSION="107.0.5304.87-1"
+ENV CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb"
+ENV CHROME_URL="https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-build-images/packages/generic/google-chrome-stable/${CHROME_VERSION}/${CHROME_DEB}"
+
+WORKDIR /home/gdk/gdk
+
+COPY --chown=gdk qa/gdk/gdk.yml .
+
+RUN cat gdk.yml && \
+ gdk update && \
+ gdk restart && \
+ ./support/test_url http://gdk.test:3000 && \
+ gdk stop && sleep 5 && \
+ GDK_KILL_CONFIRM=true gdk kill && \
+ ps -ef && \
+ cd gitlab && git reset --hard && \
+ sudo rm -rf "$HOME/gdk/gitaly/_build/deps/git/source" \
+ "$HOME/gdk/gitaly/_build/deps/libgit2/source" \
+ "$HOME/gdk/gitaly/_build/cache" \
+ "$HOME/gdk/gitaly/_build/deps" \
+ "$HOME/gdk/gitaly/_build/intermediate" \
+ "$HOME/.cache/" \
+ "$HOME/gdk/gdk/gitlab" \
+ /tmp/*
+
+# Install Google Chrome version with headless support
+# Download from our local S3 bucket, populated by https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/scripts/cache-google-chrome
+#
+RUN echo "${CHROME_URL}" && \
+ curl --silent --show-error --fail -O "${CHROME_URL}" && \
+ sudo apt update && \
+ sudo dpkg -i "./${CHROME_DEB}" || true && \
+ sudo apt install -f -y && \
+ rm -f "./${CHROME_DEB}"
+
+WORKDIR /home/gdk/gdk/gitlab
+
+RUN bundle install --jobs=$(nproc) --retry=3 --quiet
+RUN cd qa && \
+ bundle install --jobs=$(nproc) --retry=3 --quiet && \
+ bundle exec rake -f tasks/webdrivers.rake webdrivers:chromedriver:update[${CHROME_DRIVER_VERSION}]
+
+RUN git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
+
+COPY --chown=gdk qa/gdk/launch .
+
+ENTRYPOINT ["./launch"]
diff --git a/qa/gdk/gdk.yml b/qa/gdk/gdk.yml
new file mode 100644
index 00000000000..0494cd0d3c1
--- /dev/null
+++ b/qa/gdk/gdk.yml
@@ -0,0 +1,26 @@
+---
+hostname: gdk.test
+sshd:
+ additional_config: 'AcceptEnv GIT_PROTOCOL'
+webpack:
+ live_reload: false
+ sourcemaps: false
+ incremental: false
+gdk:
+ ask_to_restart_after_update: false
+ auto_reconfigure: false
+ overwrite_changes: true
+ quiet: false
+gitlab:
+ rails:
+ bootsnap: false
+ hostname: gdk.test
+gitlab_k8s_agent:
+ enabled: false
+gitlab_pages:
+ enabled: false
+prometheus:
+ enabled: false
+tracer:
+ jaeger:
+ enabled: false
diff --git a/qa/gdk/launch b/qa/gdk/launch
new file mode 100755
index 00000000000..4b1fc6ae191
--- /dev/null
+++ b/qa/gdk/launch
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+COMMIT_REF=${1:-$CI_COMMIT_REF_SLUG}
+RSPEC_ARGS=$2
+
+if [ -z "${COMMIT_REF}" ]; then
+ echo "Please provide a commit ref with the code to be tested as the first argument"
+ exit 1
+fi
+
+# Set the GitLab license mode to "test" so that GitLab uses the appropriate encryption key
+export GITLAB_LICENSE_MODE="test"
+
+# Create the temporary directory that screenshots are saved to
+sudo install -m 777 -d /home/gdk/gdk/gitlab/qa/tmp
+
+# Update GDK
+(cd .. ; gdk update ; cat gdk.yml)
+
+# Reset, fetch, and checkout the GitLab repository with the code from the ref to be tested
+git reset --hard
+git fetch origin $COMMIT_REF
+git checkout $COMMIT_REF
+
+# Install the required gems
+bundle install --jobs=$(nproc) --retry=3 --quiet
+
+# Run the database migrations
+bundle exec rake db:migrate
+
+# Restart GDK to be sure any changes are accounted for in running services, start any stopped services, and wait until the GDK is reachable
+(cd .. ; gdk restart ; ./support/test_url http://gdk.test:3000)
+
+# Install the required gems in the QA directory
+cd qa
+bundle install --jobs=$(nproc) --retry=3 --quiet
+
+# Run the tests
+bundle exec rake "knapsack:download[test]"
+bundle exec bin/qa Test::Instance::All http://gdk.test:3000 -- $RSPEC_ARGS || true
diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
index 62f55aea2cc..3cb501efe13 100644
--- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb
+++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb
@@ -32,8 +32,7 @@ module Gitlab
div :project
div :storage_type_legend
span :container_registry_size
- div :purchased_usage_total_free # Different UI for free namespace
- span :purchased_usage_total
+ div :purchased_usage_total
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
div :additional_storage_alert, text: /purchase additional storage/
@@ -66,14 +65,10 @@ module Gitlab
# Returns total purchased storage value once it's ready on page
#
# @return [Float] Total purchased storage value in GiB
- def total_purchased_storage(free_name_space = true)
+ def total_purchased_storage
additional_storage_alert_element.wait_until(&:present?)
- if free_name_space
- purchased_usage_total_free.split('/').last.match(/\d+\.\d+/)[0].to_f
- else
- purchased_usage_total.to_f
- end
+ purchased_usage_total[/(\d+){2}.\d+/].to_f
end
def additional_ci_minutes_added?
diff --git a/qa/qa/page/trials/new.rb b/qa/lib/gitlab/page/trials/new.rb
index 40f593a7aa7..b2e6cbdb682 100644
--- a/qa/qa/page/trials/new.rb
+++ b/qa/lib/gitlab/page/trials/new.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module QA
+module Gitlab
module Page
module Trials
class New < Chemlab::Page
diff --git a/qa/qa/page/trials/select.rb b/qa/lib/gitlab/page/trials/select.rb
index 39ef604a781..6eaf6003837 100644
--- a/qa/qa/page/trials/select.rb
+++ b/qa/lib/gitlab/page/trials/select.rb
@@ -1,16 +1,23 @@
# frozen_string_literal: true
-module QA
+module Gitlab
module Page
module Trials
class Select < Chemlab::Page
path '/-/trials/select'
- select :subscription_for
+ button :select_group, 'data-testid': 'base-dropdown-toggle'
+ div :group_dropdown, 'data-testid': 'base-dropdown-menu'
text_field :new_group_name
button :start_your_free_trial
radio :trial_company
radio :trial_individual
+
+ def subscription_for=(group_name)
+ select_group
+
+ group_dropdown_element.span(text: /#{group_name}/).click
+ end
end
end
end
diff --git a/qa/lib/slack.rb b/qa/lib/slack.rb
new file mode 100644
index 00000000000..95e81b700f5
--- /dev/null
+++ b/qa/lib/slack.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'chemlab/library'
+
+module Slack
+ include Chemlab::Library
+
+ self.base_url = 'https://slack.com'
+end
diff --git a/qa/lib/slack/mixins/browser.rb b/qa/lib/slack/mixins/browser.rb
new file mode 100644
index 00000000000..853ff2c7130
--- /dev/null
+++ b/qa/lib/slack/mixins/browser.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Slack
+ module Mixins
+ module Browser
+ def browser
+ ::Chemlab.configuration.browser.session.engine
+ end
+ end
+ end
+end
diff --git a/qa/lib/slack/mixins/gitlab_app.rb b/qa/lib/slack/mixins/gitlab_app.rb
new file mode 100644
index 00000000000..66b456ef824
--- /dev/null
+++ b/qa/lib/slack/mixins/gitlab_app.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Slack
+ module Mixins
+ module GitlabApp
+ # @param [QA::Resource::Project] project
+ # @param [String] channel
+ # @param [String] title
+ # @param [String] description
+ def create_issue(project, channel:, title:, description:)
+ lines = [
+ "/staging-gitlab #{project.path_with_namespace} issue new #{title}",
+ description
+ ]
+
+ send_message_to_channel(lines, channel: channel)
+ end
+
+ # @param [QA::Resource::Project] project
+ # @param [QA::Resource::Project] target
+ # @param [String] id
+ # @param [String] channel
+ def move_issue(project, target, id:, channel:)
+ line = "/staging-gitlab #{project.path_with_namespace} issue move #{id} to #{target.path_with_namespace}"
+ send_message_to_channel([line], channel: channel)
+ end
+
+ # @param [QA::Resource::Project] project
+ # @param [String] id
+ # @param [String] channel
+ def show_issue(project, id:, channel:)
+ send_message_to_channel(["/staging-gitlab #{project.path_with_namespace} issue show #{id}"], channel: channel)
+ end
+
+ # @param [QA::Resource::Project] project
+ # @param [String] id
+ # @param [String] channel
+ def close_issue(project, id:, channel:)
+ send_message_to_channel(["/staging-gitlab #{project.path_with_namespace} issue close #{id}"], channel: channel)
+ end
+
+ # @param [QA::Resource::Project] project
+ # @param [String] channel
+ # @param [String] id
+ # @param [String] comment
+ def comment_on_issue(project, channel:, id:, comment:)
+ command = "/staging-gitlab #{project.path_with_namespace} issue comment #{id}"
+ send_message_to_channel([command, comment], channel: channel)
+ end
+ end
+ end
+end
diff --git a/qa/lib/slack/page/chat.rb b/qa/lib/slack/page/chat.rb
new file mode 100644
index 00000000000..5f8553fc425
--- /dev/null
+++ b/qa/lib/slack/page/chat.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Slack
+ module Page
+ class Chat < Chemlab::Page
+ include Mixins::Browser
+ include Mixins::GitlabApp
+
+ div :message_field, data_qa: 'message_input'
+ button :connect_gitlab_button, visible_text: /Connect your GitLab account/
+ button :skip_download_slack_button, data_qa: 'continue_in_browser'
+
+ def skip_download_screen
+ wait_for_text('download the Slack app')
+
+ skip_download_slack_button_element.click if skip_download_slack_button_element.exists?
+ end
+
+ # @param [Array<String>] lines - messages to send
+ # @param [String] channel to send message to
+ def send_message_to_channel(lines, channel:)
+ go_to_channel(channel)
+
+ message_field_element.focus
+ message_field_element.click
+
+ while line = lines.shift
+ browser.send_keys(line)
+ wait_for_text(line)
+
+ browser.send_keys([:shift, :enter]) unless lines.empty?
+ end
+
+ browser.send_keys(:enter)
+ end
+
+ def wait_for_text(line)
+ QA::Support::Waiter.wait_until(max_duration: 3, raise_on_failure: false) do
+ browser.text.include?(line)
+ end
+ end
+
+ def go_to_channel(channel)
+ menu_item = messages.find do |div|
+ div.text == channel
+ end
+ menu_item.click
+ end
+
+ def click_connect_account_link
+ divs = messages(visible_text: /connect your GitLab account/i)
+ el = divs.last.a(href: /staging-ref/)
+ el.scroll.to(:top)
+ el.click
+ end
+
+ def messages(**opts)
+ browser.divs(data_qa: 'virtual-list-item', **opts)
+ end
+
+ def gitlab_app_home
+ browser.divs(data_qa: 'channel_item_container').find do |el|
+ el.text == 'GitLab'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/lib/slack/page/login.rb b/qa/lib/slack/page/login.rb
new file mode 100644
index 00000000000..a11b0d27fd0
--- /dev/null
+++ b/qa/lib/slack/page/login.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Slack
+ module Page
+ class Login < Chemlab::Page
+ path '/workspace-signin'
+
+ text_field :workspace_field, data_qa: 'signin_domain_input'
+ button :continue_button, data_qa: 'submit_team_domain_button'
+
+ link :sign_in_with_password_link, data_qa: 'sign_in_password_link'
+
+ text_field :email_address_field, data_qa: 'login_email'
+ text_field :password_field, data_qa: 'login_password', type: 'password'
+ button :sign_in_button, data_qa: 'signin_button'
+
+ def sign_in
+ navigate_to_workspace
+
+ # sign into with password if needed
+ sign_in_with_password_link_element.click if sign_in_with_password_link_element.exists?
+
+ finish_sign_in
+ end
+
+ def navigate_to_workspace
+ self.workspace_field = ::QA::Runtime::Env.slack_workspace
+ continue_button
+ end
+
+ def finish_sign_in
+ return unless email_address_field_element.exists?
+
+ self.email_address_field = ::QA::Runtime::Env.slack_email
+ self.password_field = ::QA::Runtime::Env.slack_password
+ sign_in_button
+ end
+ end
+ end
+end
diff --git a/qa/lib/slack/page/oauth.rb b/qa/lib/slack/page/oauth.rb
new file mode 100644
index 00000000000..700d19f0c4c
--- /dev/null
+++ b/qa/lib/slack/page/oauth.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Slack
+ module Page
+ class Oauth < Chemlab::Page
+ button :submit_oauth, data_qa: 'oauth_submit_button'
+ end
+ end
+end
diff --git a/qa/qa.rb b/qa/qa.rb
index fe0c19e0818..cb1278771d9 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -85,11 +85,6 @@ module QA
"fips" => "FIPS"
)
- # Configure knapsack at the very begining of the setup
- loader.on_setup do
- QA::Support::KnapsackReport.configure!
- end
-
loader.setup
loader.eager_load
end
diff --git a/qa/qa/fixtures/gpg/admin.asc b/qa/qa/fixtures/gpg/admin.asc
new file mode 100644
index 00000000000..d8daa50cfc5
--- /dev/null
+++ b/qa/qa/fixtures/gpg/admin.asc
Binary files differ
diff --git a/qa/qa/fixtures/package_managers/helm/helm_install_package.yaml.erb b/qa/qa/fixtures/package_managers/helm/helm_install_package.yaml.erb
index 590120ce7b2..f4efbebe264 100644
--- a/qa/qa/fixtures/package_managers/helm/helm_install_package.yaml.erb
+++ b/qa/qa/fixtures/package_managers/helm/helm_install_package.yaml.erb
@@ -1,6 +1,7 @@
pull:
- image: dtzar/helm-kubectl:latest
+ image: alpine:latest
script:
+ - apk add helm --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing
- helm repo add --username <%= username %> --password <%= access_token %> gitlab_qa ${CI_API_V4_URL}/projects/<%= package_project.id %>/packages/helm/stable
- helm repo update
- helm pull gitlab_qa/<%= package_name %>
diff --git a/qa/qa/fixtures/package_managers/helm/helm_upload_package.yaml.erb b/qa/qa/fixtures/package_managers/helm/helm_upload_package.yaml.erb
index b1c275cd96a..cd218fcc4a2 100644
--- a/qa/qa/fixtures/package_managers/helm/helm_upload_package.yaml.erb
+++ b/qa/qa/fixtures/package_managers/helm/helm_upload_package.yaml.erb
@@ -1,6 +1,7 @@
deploy:
- image: dtzar/helm-kubectl:latest
+ image: alpine:latest
script:
+ - apk add helm --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing
- apk add curl
- helm create <%= package_name %>
- cp ./Chart.yaml <%= package_name %>
diff --git a/qa/qa/flow/alert_settings.rb b/qa/qa/flow/alert_settings.rb
index 2a464c16c6d..f5ee4f94065 100644
--- a/qa/qa/flow/alert_settings.rb
+++ b/qa/qa/flow/alert_settings.rb
@@ -5,13 +5,11 @@ module QA
module AlertSettings
extend self
- def setup_http_endpoint(
- integration_name: random_word,
- payload: { title: random_word, description: random_word },
- send: true
- )
- credentials = {}
+ def go_to_monitor_settings
Page::Project::Menu.perform(&:go_to_monitor_settings)
+ end
+
+ def setup_http_endpoint_integration(integration_name: random_word)
Page::Project::Settings::Monitor.perform do |setting|
setting.expand_alerts do |alert|
alert.add_new_integration
@@ -19,23 +17,11 @@ module QA
alert.enter_integration_name(integration_name)
alert.activate_integration
alert.save_and_create_alert
-
- if send
- alert.fill_in_test_payload(payload.to_json)
- alert.send_test_alert
- else
- alert.go_to_view_credentials
- credentials = { url: alert.webhook_url, auth_key: alert.authorization_key }
- end
end
end
-
- credentials
end
- def setup_prometheus(payload: { title: random_word, description: random_word }, send: true)
- credentials = {}
- Page::Project::Menu.perform(&:go_to_monitor_settings)
+ def setup_prometheus_integration
Page::Project::Settings::Monitor.perform do |setting|
setting.expand_alerts do |alert|
alert.add_new_integration
@@ -43,25 +29,87 @@ module QA
alert.activate_integration
alert.fill_in_prometheus_url
alert.save_and_create_alert
-
- if send
- alert.fill_in_test_payload(payload.to_json)
- alert.send_test_alert
- else
- alert.go_to_view_credentials
- credentials = { url: alert.webhook_url, auth_key: alert.authorization_key }
- end
end
end
+ end
+
+ def send_test_alert(integration_type: 'http', payload: nil)
+ payload ||= integration_type == 'http' ? http_payload : prometheus_payload
+
+ Page::Project::Settings::Alerts.perform do |alert|
+ alert.fill_in_test_payload(payload.to_json)
+ alert.send_test_alert
+ end
+ end
+
+ def integration_credentials
+ credentials = {}
+ Page::Project::Settings::Alerts.perform do |alert|
+ alert.go_to_view_credentials
+ credentials = { url: alert.webhook_url, auth_key: alert.authorization_key }
+ end
credentials
end
+ def enable_create_incident
+ Page::Project::Settings::Monitor.perform do |setting|
+ setting.expand_alerts do |alert|
+ alert.go_to_alert_settings
+ alert.enable_incident_for_alert
+ alert.save_alert_settings
+ alert.click_button('Collapse')
+ end
+ end
+ end
+
+ def enable_email_notification
+ Page::Project::Settings::Monitor.perform do |setting|
+ setting.expand_alerts do |alert|
+ alert.go_to_alert_settings
+ alert.enable_email_notification
+ alert.save_alert_settings
+ alert.click_button('Collapse')
+ end
+ end
+ end
+
private
def random_word
Faker::Lorem.word
end
+
+ def http_payload
+ { title: random_word, description: random_word }
+ end
+
+ def prometheus_payload
+ {
+ version: '4',
+ groupKey: nil,
+ status: 'firing',
+ receiver: '',
+ groupLabels: {},
+ commonLabels: {},
+ commonAnnotations: {},
+ externalURL: '',
+ alerts: [
+ {
+ startsAt: Time.now,
+ generatorURL: Faker::Internet.url,
+ endsAt: nil,
+ status: 'firing',
+ labels: { gitlab_environment_name: Faker::Lorem.word },
+ annotations:
+ {
+ title: random_word,
+ gitlab_y_label: 'status'
+ }
+ }
+ ]
+ }
+ end
end
end
end
diff --git a/qa/qa/flow/integrations/slack.rb b/qa/qa/flow/integrations/slack.rb
new file mode 100644
index 00000000000..8f18ccaa791
--- /dev/null
+++ b/qa/qa/flow/integrations/slack.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module Integrations
+ module Slack
+ extend self
+
+ # Need to sign in for this method
+ # @param [QA::Resource::Project]
+ def start_slack_install(project)
+ project.visit!
+
+ Page::Project::Menu.perform do |project_menu_page|
+ project_menu_page.click_project
+ project_menu_page.go_to_integrations_settings
+ end
+
+ Page::Project::Settings::Integrations.perform(&:click_slack_application_link)
+
+ EE::Page::Project::Settings::Services::Slack.perform(&:start_slack_install)
+ ::Slack::Page::Oauth.perform(&:submit_oauth)
+ end
+
+ # @param [QA::Resource::Project] project
+ # @option [String | Nil] channel
+ # @return [Boolean] is this account already authorized?
+ def start_gitlab_connect(project, channel: nil)
+ ::Slack::Page::Chat.perform do |chat_page|
+ # sometimes Slack will present a blocking page
+ # for downloading the app instead of using a browser
+ chat_page.skip_download_screen
+
+ lines = ["/staging-gitlab #{project.path_with_namespace} issue show 1"]
+ chat_page.send_message_to_channel(lines, channel: channel)
+
+ # The only way to know if we are authorized is to send a slash command to the channel.
+ # If the account / chat_name is already authorized, the Slack app will try to look up the issue
+ # and return a 404 because it doesn't exist
+ QA::Support::Waiter.wait_until(max_duration: 4, raise_on_failure: false) do
+ chat_page.messages.last.text =~ /connect your GitLab account|404 not found!/i
+ end
+
+ break(true) if chat_page.messages.last.text =~ /404 not found!/i
+
+ chat_page.click_connect_account_link
+
+ false
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/flow/trial.rb b/qa/qa/flow/trial.rb
new file mode 100644
index 00000000000..109afeffaa3
--- /dev/null
+++ b/qa/qa/flow/trial.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module Trial
+ extend self
+
+ CUSTOMER_TRIAL_INFO = {
+ company_name: 'QA Test Company',
+ number_of_employees: '500 - 1,999',
+ telephone_number: '555-555-5555',
+ country: 'United States of America',
+ state: 'CA'
+ }.freeze
+
+ def register_for_trial(group: nil)
+ Gitlab::Page::Trials::New.perform do |new|
+ new.company_name = CUSTOMER_TRIAL_INFO[:company_name]
+ new.number_of_employees = CUSTOMER_TRIAL_INFO[:number_of_employees]
+ new.country = CUSTOMER_TRIAL_INFO[:country]
+ new.telephone_number = CUSTOMER_TRIAL_INFO[:telephone_number]
+ new.state = CUSTOMER_TRIAL_INFO[:state]
+
+ new.continue
+ end
+
+ return unless group
+
+ Gitlab::Page::Trials::Select.perform do |select|
+ select.subscription_for = group.path
+ select.trial_company
+ select.start_your_free_trial
+ end
+ end
+ end
+ end
+end
+
+QA::Flow::Purchase.prepend_mod_with('Flow::Trial', namespace: QA)
diff --git a/qa/qa/flow/user_onboarding.rb b/qa/qa/flow/user_onboarding.rb
index 62397d5641d..8c6a50f251f 100644
--- a/qa/qa/flow/user_onboarding.rb
+++ b/qa/qa/flow/user_onboarding.rb
@@ -10,6 +10,7 @@ module QA
if welcome_page.has_get_started_button?
welcome_page.select_role('Other')
welcome_page.choose_setup_for_company_if_available
+ welcome_page.choose_create_a_new_project_if_available
welcome_page.click_get_started_button
end
end
diff --git a/qa/qa/page/component/blob_content.rb b/qa/qa/page/component/blob_content.rb
index a57ef38f768..5ee5d4978e5 100644
--- a/qa/qa/page/component/blob_content.rb
+++ b/qa/qa/page/component/blob_content.rb
@@ -22,7 +22,7 @@ module QA
element :copy_contents_button
end
- base.view 'app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue' do
+ base.view 'app/assets/javascripts/vue_shared/components/source_viewer/source_viewer_deprecated.vue' do
element :blob_viewer_file_content
end
end
diff --git a/qa/qa/page/component/design_management.rb b/qa/qa/page/component/design_management.rb
index 90c86688882..71c24b6cac8 100644
--- a/qa/qa/page/component/design_management.rb
+++ b/qa/qa/page/component/design_management.rb
@@ -78,7 +78,7 @@ module QA
end
def update_design(filename)
- filepath = ::File.join('qa', 'fixtures', 'designs', 'update', filename)
+ filepath = ::File.join(Runtime::Path.fixtures_path, 'designs', 'update', filename)
add_design(filepath)
end
diff --git a/qa/qa/page/component/dropdown.rb b/qa/qa/page/component/dropdown.rb
index c3e0fefee0d..01ef3533ff8 100644
--- a/qa/qa/page/component/dropdown.rb
+++ b/qa/qa/page/component/dropdown.rb
@@ -4,36 +4,24 @@ module QA
module Page
module Component
module Dropdown
- include Select2
-
def select_item(item_text)
- return super if use_select2?
-
- find('li.gl-dropdown-item', text: item_text, match: :prefer_exact).click
+ find('li.gl-new-dropdown-item', text: item_text, match: :prefer_exact).click
end
def has_item?(item_text)
- return super if use_select2?
-
- has_css?('li.gl-dropdown-item', text: item_text, match: :prefer_exact)
+ has_css?('li.gl-new-dropdown-item', text: item_text, match: :prefer_exact)
end
def current_selection
- return super if use_select2?
-
expand_select_list unless dropdown_open?
- find('span.gl-dropdown-button-text').text
+ find('span.gl-new-dropdown-button-text').text
end
def all_items
- raise NotImplementedError if use_select2?
-
- find_all("li.gl-dropdown-item").map(&:text)
+ find_all("li.gl-new-dropdown-item").map(&:text)
end
def clear_current_selection_if_present
- return super if use_select2?
-
expand_select_list unless dropdown_open?
if has_css?('button[data-testid="listbox-reset-button"]')
@@ -44,9 +32,9 @@ module QA
end
def search_item(item_text)
- return super if use_select2?
+ QA::Runtime::Logger.info "Searching in dropdown: \"#{item_text}\""
- find('div.gl-listbox-search input[type="Search"]').set(item_text)
+ find('div.gl-listbox-search input[type="Search"]').set(item_text, rapid: false)
wait_for_search_to_complete
end
@@ -56,10 +44,6 @@ module QA
end
def search_and_select(item_text)
- return super if use_select2?
-
- QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
-
search_item(item_text)
unless has_item?(item_text)
@@ -70,9 +54,7 @@ module QA
end
def search_and_select_exact(item_text)
- return super if use_select2?
-
- QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
+ QA::Runtime::Logger.info "Searching and selecting exact: \"#{item_text}\""
search_item(item_text)
@@ -80,18 +62,14 @@ module QA
raise QA::Page::Base::ElementNotFound, %(Couldn't find option named "#{item_text}")
end
- find('li.gl-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click
+ find('li.gl-new-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click
end
def expand_select_list
- return super if use_select2?
-
- find('svg.dropdown-chevron').click
+ find('.gl-new-dropdown-toggle').click
end
def wait_for_search_to_complete
- return super if use_select2?
-
Support::WaitForRequests.wait_for_requests
has_css?('div[data-testid="listbox-search-loader"]', wait: 1)
@@ -99,23 +77,12 @@ module QA
end
def dropdown_open?
- return super if use_select2?
-
- has_css?('ul.gl-dropdown-contents', wait: 1)
+ has_css?('ul.gl-new-dropdown-contents', wait: 1)
end
def find_input_by_prefix_and_set(element_prefix, item_text)
find("input[id^=\"#{element_prefix}\"]").set(item_text)
end
-
- private
-
- # rubocop:disable Gitlab/PredicateMemoization
- def use_select2?
- @use_select2 ||= has_css?('.select2-container', wait: 1)
- end
-
- # rubocop:enable Gitlab/PredicateMemoization
end
end
end
diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb
deleted file mode 100644
index eb791594d22..00000000000
--- a/qa/qa/page/component/invite_members_modal.rb
+++ /dev/null
@@ -1,90 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Component
- module InviteMembersModal
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.view 'app/assets/javascripts/invite_members/components/invite_modal_base.vue' do
- element :invite_button
- element :access_level_dropdown
- element :invite_members_modal_content
- end
-
- base.view 'app/assets/javascripts/invite_members/components/group_select.vue' do
- element :group_select_dropdown_search_field
- element :group_select_dropdown_item
- end
-
- base.view 'app/assets/javascripts/invite_members/components/members_token_select.vue' do
- element :members_token_select_input
- end
-
- base.view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do
- element :invite_a_group_button
- end
-
- base.view 'app/assets/javascripts/invite_members/constants.js' do
- element :invite_members_button
- end
- end
-
- def open_invite_members_modal
- click_element :invite_members_button
- end
-
- def open_invite_group_modal
- click_element :invite_a_group_button
- end
-
- def add_member(username, access_level = 'Developer', refresh_page: true)
- open_invite_members_modal
-
- within_element(:invite_members_modal_content) do
- fill_element(:members_token_select_input, username)
- Support::WaitForRequests.wait_for_requests
- click_button(username, match: :prefer_exact)
- set_access_level(access_level)
- end
-
- send_invite(refresh_page)
- end
-
- def invite_group(group_name, access_level = 'Guest', refresh_page: true)
- open_invite_group_modal
-
- within_element(:invite_members_modal_content) do
- click_button 'Select a group'
-
- Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) }
-
- fill_element :group_select_dropdown_search_field, group_name
- Support::WaitForRequests.wait_for_requests
- click_button group_name
-
- set_access_level(access_level)
- end
-
- send_invite(refresh_page)
- end
-
- def send_invite(refresh = false)
- click_element :invite_button
- Support::WaitForRequests.wait_for_requests
- page.refresh if refresh
- end
-
- private
-
- def set_access_level(access_level)
- # Guest option is selected by default, skipping these steps if desired option is 'Guest'
- select_element(:access_level_dropdown, access_level) unless access_level == 'Guest'
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/component/members/invite_members_modal.rb b/qa/qa/page/component/members/invite_members_modal.rb
new file mode 100644
index 00000000000..0220bb1e4fc
--- /dev/null
+++ b/qa/qa/page/component/members/invite_members_modal.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Members
+ module InviteMembersModal
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/assets/javascripts/invite_members/components/invite_modal_base.vue' do
+ element :invite_button
+ element :access_level_dropdown
+ element :invite_members_modal_content
+ end
+
+ base.view 'app/assets/javascripts/invite_members/components/group_select.vue' do
+ element :group_select_dropdown_search_field
+ element :group_select_dropdown_item
+ end
+
+ base.view 'app/assets/javascripts/invite_members/components/members_token_select.vue' do
+ element :members_token_select_input
+ end
+
+ base.view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do
+ element :invite_a_group_button
+ end
+
+ base.view 'app/assets/javascripts/invite_members/constants.js' do
+ element :invite_members_button
+ end
+ end
+
+ def open_invite_members_modal
+ click_element :invite_members_button
+ end
+
+ def open_invite_group_modal
+ click_element :invite_a_group_button
+ end
+
+ def add_member(username, access_level = 'Developer', refresh_page: true)
+ open_invite_members_modal
+
+ within_element(:invite_members_modal_content) do
+ fill_element(:members_token_select_input, username)
+ Support::WaitForRequests.wait_for_requests
+ click_button(username, match: :prefer_exact)
+ set_access_level(access_level)
+ end
+
+ send_invite(refresh_page)
+ end
+
+ def invite_group(group_name, access_level = 'Guest', refresh_page: true)
+ open_invite_group_modal
+
+ within_element(:invite_members_modal_content) do
+ click_button 'Select a group'
+
+ Support::Waiter.wait_until { has_element?(:group_select_dropdown_item) }
+
+ fill_element :group_select_dropdown_search_field, group_name
+ Support::WaitForRequests.wait_for_requests
+ click_button group_name
+
+ set_access_level(access_level)
+ end
+
+ send_invite(refresh_page)
+ end
+
+ def send_invite(refresh = false)
+ click_element :invite_button
+ Support::WaitForRequests.wait_for_requests
+ page.refresh if refresh
+ end
+
+ private
+
+ def set_access_level(access_level)
+ # Guest option is selected by default, skipping these steps if desired option is 'Guest'
+ select_element(:access_level_dropdown, access_level) unless access_level == 'Guest'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/members/members_filter.rb b/qa/qa/page/component/members/members_filter.rb
new file mode 100644
index 00000000000..8803211ea86
--- /dev/null
+++ b/qa/qa/page/component/members/members_filter.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Members
+ module MembersFilter
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue' do
+ element :search_bar_input
+ element :search_button
+ end
+ end
+
+ def search_member(username)
+ fill_element :search_bar_input, username
+ click_element :search_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/members/members_table.rb b/qa/qa/page/component/members/members_table.rb
new file mode 100644
index 00000000000..46010a0f9ab
--- /dev/null
+++ b/qa/qa/page/component/members/members_table.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Members
+ module MembersTable
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.class_eval do
+ include MembersFilter
+ include RemoveMemberModal
+ include RemoveGroupModal
+ end
+
+ base.view 'app/assets/javascripts/members/components/table/members_table.vue' do
+ element :member_row
+ end
+
+ base.view 'app/assets/javascripts/members/components/table/role_dropdown.vue' do
+ element :access_level_dropdown
+ element :access_level_link
+ end
+
+ base.view 'app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue' do
+ element :user_action_dropdown
+ end
+
+ base.view 'app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue' do
+ element :delete_member_dropdown_item
+ end
+
+ base.view 'app/assets/javascripts/members/components/action_buttons/approve_access_request_button.vue' do
+ element :approve_access_request_button
+ end
+
+ base.view 'app/assets/javascripts/members/components/members_tabs.vue' do
+ element :groups_list_tab
+ end
+
+ base.view 'app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue' do
+ element :remove_group_link_button
+ end
+ end
+
+ def update_access_level(username, access_level)
+ search_member(username)
+
+ within_element(:member_row, text: username) do
+ click_element :access_level_dropdown
+ click_element :access_level_link, text: access_level
+ end
+ end
+
+ def remove_member(username)
+ within_element(:member_row, text: username) do
+ click_element :user_action_dropdown
+ click_element :delete_member_dropdown_item
+ end
+
+ confirm_remove_member
+ end
+
+ def approve_access_request(username)
+ within_element(:member_row, text: username) do
+ click_element :approve_access_request_button
+ end
+ end
+
+ def deny_access_request(username)
+ within_element(:member_row, text: username) do
+ click_element :delete_member_button
+ end
+
+ confirm_remove_member
+ end
+
+ def remove_group(group_name)
+ click_element :groups_list_tab
+
+ within_element(:member_row, text: group_name) do
+ click_element :remove_group_link_button
+ end
+
+ confirm_remove_group
+ end
+
+ def has_group?(group_name)
+ click_element :groups_list_tab
+ has_element?(:member_row, text: group_name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/members/remove_group_modal.rb b/qa/qa/page/component/members/remove_group_modal.rb
new file mode 100644
index 00000000000..03c51757b62
--- /dev/null
+++ b/qa/qa/page/component/members/remove_group_modal.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Members
+ module RemoveGroupModal
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/assets/javascripts/members/components/modals/remove_group_link_modal.vue' do
+ element :remove_group_link_modal_content
+ element :remove_group_button
+ end
+ end
+
+ def confirm_remove_group
+ within_element(:remove_group_link_modal_content) do
+ wait_for_enabled_remove_group_button
+
+ click_element :remove_group_button
+ end
+ end
+
+ private
+
+ def wait_for_enabled_remove_group_button
+ retry_until(sleep_interval: 1, message: 'Waiting for remove group button to be enabled') do
+ has_element?(:remove_group_button, disabled: false, wait: 3)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/members/remove_member_modal.rb b/qa/qa/page/component/members/remove_member_modal.rb
new file mode 100644
index 00000000000..b7b81040ba7
--- /dev/null
+++ b/qa/qa/page/component/members/remove_member_modal.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Component
+ module Members
+ module RemoveMemberModal
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
+ element :remove_member_modal
+ element :remove_member_button
+ end
+ end
+
+ def confirm_remove_member
+ within_element(:remove_member_modal) do
+ wait_for_enabled_remove_member_button
+
+ click_element :remove_member_button
+ end
+ end
+
+ private
+
+ def wait_for_enabled_remove_member_button
+ retry_until(sleep_interval: 1, message: 'Waiting for remove member button to be enabled') do
+ has_element?(:remove_member_button, disabled: false, wait: 3)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/component/members_filter.rb b/qa/qa/page/component/members_filter.rb
deleted file mode 100644
index fce4560d255..00000000000
--- a/qa/qa/page/component/members_filter.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Component
- module MembersFilter
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.view 'app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue' do
- element :search_bar_input
- element :search_button
- end
- end
-
- def search_member(username)
- fill_element :search_bar_input, username
- click_element :search_button
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
deleted file mode 100644
index 7a835af2575..00000000000
--- a/qa/qa/page/component/select2.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Component
- module Select2
- def select_item(item_text)
- find('.select2-result-label', text: item_text, match: :prefer_exact).click
- end
-
- def has_item?(item_text)
- has_css?('.select2-result-label', text: item_text, match: :prefer_exact)
- end
-
- def current_selection
- find('.select2-chosen').text
- end
-
- def clear_current_selection_if_present
- if has_css?('a > abbr.select2-search-choice-close', wait: 1.0)
- find('a > abbr.select2-search-choice-close').click
- end
- end
-
- def search_item(item_text)
- find('.select2-input').set(item_text)
-
- wait_for_search_to_complete
- end
-
- def search_and_select(item_text)
- QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
-
- search_item(item_text)
-
- raise QA::Page::Base::ElementNotFound, %Q(Couldn't find option named "#{item_text}") unless has_item?(item_text)
-
- select_item(item_text)
- end
-
- def search_and_select_exact(item_text)
- QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
-
- search_item(item_text)
-
- raise QA::Page::Base::ElementNotFound, %Q(Couldn't find option named "#{item_text}") unless has_item?(item_text)
-
- find('.select2-result-label', text: item_text, exact_text: true).click
- end
-
- def expand_select_list
- find('span.select2-arrow').click
- end
-
- def wait_for_search_to_complete
- Support::WaitForRequests.wait_for_requests
-
- has_css?('.select2-active', wait: 1)
- has_no_css?('.select2-active', wait: 30)
- end
-
- def dropdown_open?
- find('.select2-focusser').disabled?
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/component/wiki_sidebar.rb b/qa/qa/page/component/wiki_sidebar.rb
index dfb912a1d0b..7543b9655f9 100644
--- a/qa/qa/page/component/wiki_sidebar.rb
+++ b/qa/qa/page/component/wiki_sidebar.rb
@@ -20,6 +20,7 @@ module QA
base.view 'app/views/shared/wikis/_wiki_directory.html.haml' do
element :wiki_directory_content
+ element :wiki_dir_page_link
end
end
@@ -42,6 +43,10 @@ module QA
def has_directory?(directory)
has_element?(:wiki_directory_content, text: directory)
end
+
+ def has_dir_page?(dir_page)
+ has_element?(:wiki_dir_page_link, page_name: dir_page)
+ end
end
end
end
diff --git a/qa/qa/page/dashboard/todos.rb b/qa/qa/page/dashboard/todos.rb
index d5660823118..a65bba9ac39 100644
--- a/qa/qa/page/dashboard/todos.rb
+++ b/qa/qa/page/dashboard/todos.rb
@@ -8,12 +8,19 @@ module QA
view 'app/views/dashboard/todos/index.html.haml' do
element :todos_list_container, required: true
+ element :group_dropdown
end
view 'app/views/dashboard/todos/_todo.html.haml' do
element :todo_item_container
element :todo_action_name_content
element :todo_target_title_content
+ element :todo_author_name_content
+ end
+
+ view 'app/helpers/dropdowns_helper.rb' do
+ element :dropdown_input_field
+ element :dropdown_list_content
end
def has_todo_list?
@@ -24,10 +31,39 @@ module QA
has_no_element?(:todo_item_container)
end
- def has_latest_todo_item_with_content?(action, title)
+ def filter_todos_by_group(group)
+ click_element :group_dropdown
+
+ fill_element(:dropdown_input_field, group.path)
+
+ within_element(:dropdown_list_content) do
+ click_on group.path
+ end
+
+ wait_for_requests
+ end
+
+ def has_latest_todo_with_author?(author:, action:)
+ content = { selector: :todo_author_name_content, text: author }
+ has_latest_todo_with_content?(action, **content)
+ end
+
+ def has_latest_todo_with_title?(title:, action:)
+ content = { selector: :todo_target_title_content, text: title }
+ has_latest_todo_with_content?(action, **content)
+ end
+
+ def click_todo_with_content(content)
+ click_element(:todo_item_container, text: content)
+ end
+
+ private
+
+ def has_latest_todo_with_content?(action, **kwargs)
within_element(:todos_list_container) do
within_element_by_index(:todo_item_container, 0) do
- has_element?(:todo_action_name_content, text: action) && has_element?(:todo_target_title_content, text: title)
+ has_element?(:todo_action_name_content, text: action) &&
+ has_element?(kwargs[:selector], text: kwargs[:text])
end
end
end
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index c7d63b97b4f..7756d3d7f08 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -4,63 +4,8 @@ module QA
module Page
module Group
class Members < Page::Base
- include Page::Component::InviteMembersModal
- include Page::Component::MembersFilter
-
- view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
- element :remove_member_modal
- end
-
- view 'app/assets/javascripts/pages/groups/group_members/index.js' do
- element :member_row
- element :groups_list
- element :group_row
- end
-
- view 'app/assets/javascripts/members/components/table/role_dropdown.vue' do
- element :access_level_dropdown
- element :access_level_link
- end
-
- view 'app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue' do
- element :user_action_dropdown
- end
-
- view 'app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue' do
- element :delete_member_dropdown_item
- end
-
- view 'app/assets/javascripts/members/components/members_tabs.vue' do
- element :groups_list_tab
- end
-
- def update_access_level(username, access_level)
- search_member(username)
-
- within_element(:member_row, text: username) do
- click_element :access_level_dropdown
- click_element :access_level_link, text: access_level
- end
- end
-
- def remove_member(username)
- within_element(:member_row, text: username) do
- click_element :user_action_dropdown
- click_element :delete_member_dropdown_item
- end
-
- within_element(:remove_member_modal) do
- click_button("Remove member")
- end
- end
-
- def has_existing_group_share?(group_name)
- click_element :groups_list_tab
-
- within_element(:groups_list) do
- has_element?(:group_row, text: group_name)
- end
- end
+ include Page::Component::Members::InviteMembersModal
+ include Page::Component::Members::MembersTable
end
end
end
diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb
index de065ca187d..9418593133e 100644
--- a/qa/qa/page/group/menu.rb
+++ b/qa/qa/page/group/menu.rb
@@ -44,6 +44,14 @@ module QA
end
end
+ def go_to_runners
+ hover_group_ci_cd do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Runners')
+ end
+ end
+ end
+
def go_to_package_settings
hover_group_settings do
within_submenu do
@@ -120,6 +128,14 @@ module QA
end
end
+ def hover_group_ci_cd
+ within_sidebar do
+ find_element(:sidebar_menu_link, menu_item: 'CI/CD').hover
+
+ yield
+ end
+ end
+
def hover_group_packages
within_sidebar do
scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries')
diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb
index bb5a3485531..44460291c90 100644
--- a/qa/qa/page/group/settings/general.rb
+++ b/qa/qa/page/group/settings/general.rb
@@ -123,7 +123,7 @@ module QA
wait_for_enabled_transfer_group_button
click_element(:transfer_group_button)
- fill_confirmation_text(source_group.path)
+ fill_confirmation_text(source_group.full_path)
confirm_transfer
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 46ab1e35510..1aab6847b4c 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -15,6 +15,7 @@ module QA
view 'app/views/shared/members/_access_request_links.html.haml' do
element :leave_group_link
+ element :request_access_link
end
def click_subgroup(name)
@@ -46,6 +47,10 @@ module QA
click_element :leave_group_link
click_confirmation_ok_button
end
+
+ def click_request_access
+ click_element :request_access_link
+ end
end
end
end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 8af78bb86c6..f4f8820bc04 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -76,16 +76,13 @@ module QA
end
def sign_in_using_admin_credentials
- admin = QA::Resource::User.init do |user|
- user.username = QA::Runtime::User.admin_username
- user.password = QA::Runtime::User.admin_password
- end
-
using_wait_time 0 do
set_initial_password_if_present
sign_in_using_gitlab_credentials(user: admin)
end
+ set_up_new_admin_password_if_required
+
Page::Main::Menu.perform(&:has_personal_area?)
end
@@ -105,6 +102,24 @@ module QA
Page::Main::Menu.perform(&:signed_in?)
end
+ # Handle request for password change
+ # Happens on clean GDK installations when seeded root admin password is expired
+ #
+ def set_up_new_password_if_required(user:, skip_page_validation:)
+ return unless has_content?('Set up new password')
+
+ Profile::Password.perform do |new_password_page|
+ password = user&.password || Runtime::User.password
+ new_password_page.set_new_password(password, password)
+ end
+
+ sign_in_using_credentials(user: user, skip_page_validation: skip_page_validation)
+ end
+
+ def set_up_new_admin_password_if_required
+ set_up_new_password_if_required(user: admin, skip_page_validation: false)
+ end
+
def self.path
'/users/sign_in'
end
@@ -181,6 +196,13 @@ module QA
private
+ def admin
+ @admin ||= QA::Resource::User.init do |user|
+ user.username = QA::Runtime::User.admin_username
+ user.password = QA::Runtime::User.admin_password
+ end
+ end
+
def sign_in_using_gitlab_credentials(user:, skip_page_validation: false)
wait_if_retry_later
@@ -219,20 +241,6 @@ module QA
fill_element :password_field, user.password
end
- # Handle request for password change
- # Happens on clean GDK installations when seeded root admin password is expired
- #
- def set_up_new_password_if_required(user:, skip_page_validation:)
- return unless has_content?('Set up new password')
-
- Profile::Password.perform do |new_password_page|
- password = user&.password || Runtime::User.password
- new_password_page.set_new_password(password, password)
- end
-
- sign_in_using_credentials(user: user, skip_page_validation: skip_page_validation)
- end
-
def set_initial_password_if_present
return unless has_content?('Change your password')
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index dc2f908a906..a1d91621090 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -10,7 +10,10 @@ module QA
view 'app/views/projects/merge_requests/creations/_new_compare.html.haml' do
element :compare_branches_button
- element :source_branch_dropdown
+ end
+
+ view 'app/assets/javascripts/merge_requests/components/compare_dropdown.vue' do
+ element :source_branch_dropdown, ':data-qa-selector="qaSelector"' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/merge_requests/_page.html.haml' do
@@ -46,8 +49,7 @@ module QA
def select_source_branch(branch)
click_element(:source_branch_dropdown)
- fill_element(:dropdown_input_field, branch)
- click_via_capybara(:click_on, branch)
+ search_and_select(branch)
end
end
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 6fd48692730..df0c0ec4202 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -11,11 +11,6 @@ module QA
element :review_preview_dropdown
end
- # Remove once :mr_review_submit_comment ff is enabled by default
- view 'app/assets/javascripts/batch_comments/components/publish_button.vue' do
- element :submit_review_button
- end
-
view 'app/assets/javascripts/batch_comments/components/review_bar.vue' do
element :review_bar_content
end
@@ -170,16 +165,8 @@ module QA
click_element(:review_preview_dropdown)
end
- # Remove if statement once :mr_review_submit_comment ff is enabled by default
-
- if has_element?(:submit_review_dropdown, wait: 5)
- click_element(:submit_review_dropdown)
- click_element(:submit_review_button)
- else
- within_element(:review_bar_content) do
- click_element(:submit_review_button)
- end
- end
+ click_element(:submit_review_dropdown)
+ click_element(:submit_review_button)
# After clicking the button, wait for the review bar to disappear
# before moving on to the next part of the test
diff --git a/qa/qa/page/profile/chat_names/new.rb b/qa/qa/page/profile/chat_names/new.rb
new file mode 100644
index 00000000000..351d36b87d5
--- /dev/null
+++ b/qa/qa/page/profile/chat_names/new.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Profile
+ module ChatNames
+ class New < Chemlab::Page
+ button :authorize, value: /Authorize/i
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index c48b1a67d90..bb1095371a8 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -5,8 +5,6 @@ module QA
module Project
module Import
class Github < Page::Base
- include Page::Component::Select2
-
view 'app/views/import/github/new.html.haml' do
element :personal_access_token_field
element :authenticate_button
@@ -17,7 +15,7 @@ module QA
element :project_path_field
element :import_button
element :project_path_content
- element :go_to_project_button
+ element :go_to_project_link
element :import_status_indicator
end
@@ -62,9 +60,9 @@ module QA
#
# @param [String] gh_project_name
# @return [Boolean]
- def has_go_to_project_button?(gh_project_name)
+ def has_go_to_project_link?(gh_project_name)
within_element(:project_import_row, source_project: gh_project_name) do
- has_element?(:go_to_project_button)
+ has_element?(:go_to_project_link)
end
end
diff --git a/qa/qa/page/project/members.rb b/qa/qa/page/project/members.rb
index 4692f3621b8..463e3ca6fca 100644
--- a/qa/qa/page/project/members.rb
+++ b/qa/qa/page/project/members.rb
@@ -4,49 +4,8 @@ module QA
module Page
module Project
class Members < Page::Base
- include QA::Page::Component::InviteMembersModal
- include QA::Page::Component::MembersFilter
-
- view 'app/assets/javascripts/members/components/members_tabs.vue' do
- element :groups_list_tab
- end
-
- view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do
- element :invite_a_group_button
- end
-
- view 'app/assets/javascripts/invite_members/constants.js' do
- element :invite_members_button
- end
-
- view 'app/assets/javascripts/pages/projects/project_members/index.js' do
- element :group_row
- end
-
- view 'app/assets/javascripts/members/components/action_buttons/remove_group_link_button.vue' do
- element :delete_group_access_link
- end
-
- view 'app/assets/javascripts/members/components/modals/remove_group_link_modal.vue' do
- element :remove_group_link_modal_content
- end
-
- def remove_group(group_name)
- click_element :groups_list_tab
-
- within_element(:group_row, text: group_name) do
- click_element :delete_group_access_link
- end
-
- within_element(:remove_group_link_modal_content) do
- click_button 'Remove group'
- end
- end
-
- def has_group?(group_name)
- click_element :groups_list_tab
- has_element?(:group_row, text: group_name)
- end
+ include Page::Component::Members::InviteMembersModal
+ include Page::Component::Members::MembersTable
end
end
end
diff --git a/qa/qa/page/project/monitor/alerts/index.rb b/qa/qa/page/project/monitor/alerts/index.rb
index 50b69d59db7..1363fb32498 100644
--- a/qa/qa/page/project/monitor/alerts/index.rb
+++ b/qa/qa/page/project/monitor/alerts/index.rb
@@ -11,7 +11,7 @@ module QA
end
def has_alert_with_title?(title)
- has_link?(title)
+ has_link?(title, wait: 5)
end
end
end
diff --git a/qa/qa/page/project/monitor/incidents/index.rb b/qa/qa/page/project/monitor/incidents/index.rb
index 9317cb27562..1b30e484723 100644
--- a/qa/qa/page/project/monitor/incidents/index.rb
+++ b/qa/qa/page/project/monitor/incidents/index.rb
@@ -8,11 +8,16 @@ module QA
class Index < Page::Base
view 'app/assets/javascripts/incidents/components/incidents_list.vue' do
element :create_incident_button
+ element :incident_link
end
def create_incident
click_element :create_incident_button
end
+
+ def has_incident?(wait: Support::Repeater::DEFAULT_MAX_WAIT_TIME)
+ wait_until(max_duration: wait) { has_element?(:incident_link) }
+ end
end
end
end
diff --git a/qa/qa/page/project/secure/configuration_form.rb b/qa/qa/page/project/secure/configuration_form.rb
index 20999f7c92a..493ec08d023 100644
--- a/qa/qa/page/project/secure/configuration_form.rb
+++ b/qa/qa/page/project/secure/configuration_form.rb
@@ -5,7 +5,6 @@ module QA
module Project
module Secure
class ConfigurationForm < QA::Page::Base
- include QA::Page::Component::Select2
include QA::Page::Settings::Common
view 'app/assets/javascripts/security_configuration/components/app.vue' do
diff --git a/qa/qa/page/project/settings/alerts.rb b/qa/qa/page/project/settings/alerts.rb
index 7b1d738ec3c..901a668f082 100644
--- a/qa/qa/page/project/settings/alerts.rb
+++ b/qa/qa/page/project/settings/alerts.rb
@@ -10,6 +10,7 @@ module QA
element :incident_templates_dropdown
element :save_changes_button
element :incident_templates_item
+ element :enable_email_notification_checkbox
end
view 'app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue' do
@@ -26,8 +27,16 @@ module QA
element :prometheus_url_field
end
+ def go_to_alert_settings
+ click_link_with_text('Alert settings')
+ end
+
def enable_incident_for_alert
- check_element(:create_incident_checkbox)
+ check_element(:create_incident_checkbox, true)
+ end
+
+ def enable_email_notification
+ check_element(:enable_email_notification_checkbox, true)
end
def select_issue_template(template)
@@ -37,7 +46,7 @@ module QA
end
end
- def save_incident_settings
+ def save_alert_settings
click_element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/default_branch.rb b/qa/qa/page/project/settings/default_branch.rb
index 69ac45ce72d..a59158966c1 100644
--- a/qa/qa/page/project/settings/default_branch.rb
+++ b/qa/qa/page/project/settings/default_branch.rb
@@ -5,6 +5,8 @@ module QA
module Project
module Settings
class DefaultBranch < Page::Base
+ include ::QA::Page::Component::Dropdown
+
view 'app/views/projects/branch_defaults/_show.html.haml' do
element :save_changes_button
end
@@ -13,14 +15,9 @@ module QA
element :default_branch_dropdown
end
- view 'app/assets/javascripts/ref/components/ref_selector.vue' do
- element :ref_selector_searchbox
- end
-
def set_default_branch(branch)
- find_element(:default_branch_dropdown, visible: false).click
- find_element(:ref_selector_searchbox, visible: false).fill_in(with: branch)
- click_button branch
+ expand_select_list
+ search_and_select(branch)
end
def click_save_changes_button
diff --git a/qa/qa/page/project/settings/integrations.rb b/qa/qa/page/project/settings/integrations.rb
index 58b3badbb22..c97593312d1 100644
--- a/qa/qa/page/project/settings/integrations.rb
+++ b/qa/qa/page/project/settings/integrations.rb
@@ -10,6 +10,7 @@ module QA
element :prometheus_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
element :jira_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
element :pipelines_email_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
+ element :gitlab_slack_application_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
end
def click_on_prometheus_integration
@@ -27,6 +28,10 @@ module QA
def click_jenkins_ci_link
click_element :jenkins_link
end
+
+ def click_slack_application_link
+ click_element :gitlab_slack_application_link
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
index ca5d13abdae..8b9b72758d8 100644
--- a/qa/qa/page/project/settings/main.rb
+++ b/qa/qa/page/project/settings/main.rb
@@ -6,7 +6,6 @@ module QA
module Settings
class Main < Page::Base
include QA::Page::Settings::Common
- include Component::Select2
include SubMenus::Project
include Component::Breadcrumbs
include Layout::Flash
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index ae6a04028c9..c42a4e0bebe 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -16,7 +16,7 @@ module QA
end
view 'app/views/projects/_merge_request_pipelines_and_threads_options.html.haml' do
- element :allow_merge_if_all_discussions_are_resolved_checkbox
+ element :only_allow_merge_if_all_discussions_are_resolved_checkbox
end
def click_save_changes
@@ -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, true)
+ check_element(:only_allow_merge_if_all_discussions_are_resolved_checkbox, true)
click_save_changes
end
end
diff --git a/qa/qa/page/registration/welcome.rb b/qa/qa/page/registration/welcome.rb
index 660b33b4f5b..77cdbe8fd9e 100644
--- a/qa/qa/page/registration/welcome.rb
+++ b/qa/qa/page/registration/welcome.rb
@@ -21,6 +21,10 @@ module QA
# Only implemented in EE
end
+ def choose_create_a_new_project_if_available
+ # Only implemented in EE
+ end
+
def click_get_started_button
Support::Retrier.retry_until do
click_element :get_started_button
diff --git a/qa/qa/page/search/results.rb b/qa/qa/page/search/results.rb
index 3f7aa837d3c..769f8accfca 100644
--- a/qa/qa/page/search/results.rb
+++ b/qa/qa/page/search/results.rb
@@ -4,9 +4,9 @@ module QA
module Page
module Search
class Results < QA::Page::Base
- view 'app/views/search/_category.html.haml' do
- element :code_tab
- element :projects_tab
+ view 'app/assets/javascripts/search/sidebar/components/scope_navigation.vue' do
+ element :code_tab, ':data-qa-selector="qaSelectorValue(item)"' # rubocop:disable QA/ElementWithPattern
+ element :projects_tab, ':data-qa-selector="qaSelectorValue(item)"' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/search/results/_blob_data.html.haml' do
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb
index 44520b04e9a..d7a220bc83f 100644
--- a/qa/qa/resource/api_fabricator.rb
+++ b/qa/qa/resource/api_fabricator.rb
@@ -19,6 +19,7 @@ module QA
(respond_to?(:api_put_path) && respond_to?(:api_put_body))
end
+ # @return [String] the resource web url
def fabricate_via_api!
unless api_support?
raise NotImplementedError, "Resource #{self.class.name} does not support fabrication via the API!"
@@ -122,9 +123,10 @@ module QA
process_api_response(api_post_to(api_post_path, api_post_body))
end
- def api_post_to(post_path, post_body)
+ def api_post_to(post_path, post_body, args = {})
if post_path == "/graphql"
- graphql_response = post(Runtime::API::Request.new(api_client, post_path).url, query: post_body)
+ payload = post_body.is_a?(String) ? { query: post_body } : post_body
+ graphql_response = post(Runtime::API::Request.new(api_client, post_path).url, payload, args)
body = flatten_hash(parse_body(graphql_response))
@@ -137,9 +139,9 @@ module QA
body[:id] = body.fetch(:id).split('/').last if body.key?(:id)
- body.transform_keys { |key| key.to_s.underscore.to_sym }
+ body.deep_transform_keys { |key| key.to_s.underscore.to_sym }
else
- response = post(Runtime::API::Request.new(api_client, post_path).url, post_body)
+ response = post(Runtime::API::Request.new(api_client, post_path).url, post_body, args)
unless response.code == HTTP_STATUS_CREATED
raise(
diff --git a/qa/qa/resource/design.rb b/qa/qa/resource/design.rb
index 4a3214f3c0e..127e7ae61e3 100644
--- a/qa/qa/resource/design.rb
+++ b/qa/qa/resource/design.rb
@@ -8,9 +8,9 @@ module QA
end
attributes :id,
- :filename,
- :full_path,
- :image
+ :filename,
+ :full_path,
+ :image
def initialize
@update = false
@@ -60,16 +60,58 @@ module QA
#
# @return [String]
def api_post_body
- # TODO: design creation requires file upload via multipart/form-data request type with file passed in mutation
- # which currently isn't supported by our api implementation
- # https://gitlab.com/gitlab-org/gitlab/-/issues/366592
- raise NotImplementedError, "File uploads are not supported"
+ query = <<~GQL
+ mutation ($files: [Upload!]!, $projectPath: ID!, $iid: ID!) {
+ designManagementUpload(input: { files: $files, projectPath: $projectPath, iid: $iid }) {
+ designs {
+ id
+ fullPath
+ image
+ filename
+ webUrl
+ }
+ }
+ }
+ GQL
+ operations = {
+ query: query,
+ variables: {
+ files: nil,
+ projectPath: issue.project.full_path,
+ iid: issue.iid
+ }
+ }
+
+ {
+ operations: JSON.dump(operations),
+ map: '{"0":["variables.files"]}',
+ "0": ::File.new(filepath)
+ }
+ end
+
+ # Override api_post_to method to add multipart request option
+ #
+ # @param [String] post_path
+ # @param [Hash] post_body
+ # @param [Hash] args
+ # @return [Hash]
+ def api_post_to(post_path, post_body, args = {})
+ super(post_path, post_body, { content_type: 'multipart/form-data' })
+ end
+
+ # Return first design from fabricated design array
+ # designManagementUpload mutation doesn't support returning single design
+ #
+ # @param [Hash] api_resource
+ # @return [Hash]
+ def transform_api_resource(api_resource)
+ api_resource.key?(:designs) ? api_resource[:designs].first : api_resource
end
private
def filepath
- ::File.absolute_path(::File.join('qa', 'fixtures', 'designs', filename))
+ ::File.join(Runtime::Path.fixtures_path, 'designs', filename)
end
end
end
diff --git a/qa/qa/resource/import_project.rb b/qa/qa/resource/import_project.rb
index 105d75285f1..a490905cbd6 100644
--- a/qa/qa/resource/import_project.rb
+++ b/qa/qa/resource/import_project.rb
@@ -7,7 +7,7 @@ module QA
def initialize
@name = "ImportedProject-#{SecureRandom.hex(8)}"
- @file_path = ::File.join('qa', 'fixtures', 'export.tar.gz')
+ @file_path = ::File.join(Runtime::Path.fixtures_path, 'export.tar.gz')
end
def fabricate!
diff --git a/qa/qa/resource/members.rb b/qa/qa/resource/members.rb
index 4bf9c2bed6b..7f31808d2ff 100644
--- a/qa/qa/resource/members.rb
+++ b/qa/qa/resource/members.rb
@@ -24,7 +24,15 @@ module QA
end
def list_members
- JSON.parse(get(Runtime::API::Request.new(api_client, api_members_path).url).body)
+ parse_body(api_get_from(api_members_path))
+ end
+
+ def list_all_members
+ parse_body(api_get_from("#{api_members_path}/all"))
+ end
+
+ def find_member(username)
+ list_members.find { |member| member[:username] == username }
end
def invite_group(group, access_level = AccessLevel::GUEST)
diff --git a/qa/qa/resource/pipeline.rb b/qa/qa/resource/pipeline.rb
index 910065d76a8..7d5036c5cf4 100644
--- a/qa/qa/resource/pipeline.rb
+++ b/qa/qa/resource/pipeline.rb
@@ -10,9 +10,9 @@ module QA
end
attributes :id,
- :status,
- :ref,
- :sha
+ :status,
+ :ref,
+ :sha
# array in form
# [
@@ -49,6 +49,10 @@ module QA
"/projects/#{project.id}/pipelines/#{id}"
end
+ def api_pipeline_jobs_path
+ "#{api_get_path}/jobs"
+ end
+
def api_post_path
"/projects/#{project.id}/pipeline"
end
@@ -93,6 +97,10 @@ module QA
result[:downstream_pipeline][:id]
end
+
+ def pipeline_jobs
+ parse_body(api_get_from(api_pipeline_jobs_path))
+ end
end
end
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index 3f42c6b649e..019617325e0 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -198,6 +198,10 @@ module QA
"#{api_get_path}/pipelines"
end
+ def api_latest_pipeline_path
+ "#{api_pipelines_path}/latest"
+ end
+
def api_pipeline_schedules_path
"#{api_get_path}/pipeline_schedules"
end
@@ -400,6 +404,10 @@ module QA
auto_paginated_response(request_url(api_pipelines_path, per_page: '100'), attempts: attempts)
end
+ def latest_pipeline
+ parse_body(api_get_from(api_latest_pipeline_path))
+ end
+
def jobs
response = get(request_url(api_jobs_path))
parse_body(response)
@@ -450,7 +458,7 @@ module QA
parse_body(response)
end
- # Fetch project specific runners
+ # Fetch project runners
#
# @param [Hash] **kwargs optional query arguments, see: https://docs.gitlab.com/ee/api/runners.html#list-projects-runners
# @return [Array]
diff --git a/qa/qa/resource/project_web_hook.rb b/qa/qa/resource/project_web_hook.rb
index 8b806c42030..86e662932e1 100644
--- a/qa/qa/resource/project_web_hook.rb
+++ b/qa/qa/resource/project_web_hook.rb
@@ -16,7 +16,11 @@ module QA
confidential_note
].freeze
- attr_accessor :url, :enable_ssl, :id
+ attr_accessor :url, :enable_ssl
+
+ attribute :disabled_until
+ attribute :id
+ attribute :alert_status
attribute :project do
Project.fabricate_via_api! do |resource|
@@ -33,19 +37,28 @@ module QA
def initialize
@id = nil
@enable_ssl = false
+ @alert_status = nil
@url = nil
end
+ def fabricate_via_api!
+ resource_web_url = super
+
+ @id = api_response[:id]
+
+ resource_web_url
+ end
+
def resource_web_url(resource)
"/project/#{project.name}/~/hooks/##{resource[:id]}/edit"
end
def api_get_path
- "/projects/#{project.id}/hooks"
+ "#{api_post_path}/#{api_response[:id]}"
end
def api_post_path
- api_get_path
+ "/projects/#{project.id}/hooks"
end
def api_post_body
diff --git a/qa/qa/resource/runner_base.rb b/qa/qa/resource/runner_base.rb
index 7580aa108c9..9386a5d9765 100644
--- a/qa/qa/resource/runner_base.rb
+++ b/qa/qa/resource/runner_base.rb
@@ -122,7 +122,7 @@ module QA
end
def runner(**kwargs)
- raise("Not implemented!")
+ raise NotImplementedError
end
end
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 0398509396f..16d0f6ad380 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -12,7 +12,8 @@ module QA
:extern_uid,
:expect_fabrication_success,
:hard_delete_on_api_removal,
- :access_level
+ :access_level,
+ :email_domain
attributes :id,
:name,
@@ -25,6 +26,7 @@ module QA
@hard_delete_on_api_removal = false
@unique_id = SecureRandom.hex(8)
@expect_fabrication_success = true
+ @email_domain = 'example.com'
end
def self.default
@@ -63,7 +65,7 @@ module QA
def email
@email ||= begin
api_email = api_resource&.dig(:email)
- api_email && !api_email.empty? ? api_email : "#{username}@example.com"
+ api_email && !api_email.empty? ? api_email : "#{username}@#{email_domain}"
end
end
diff --git a/qa/qa/runtime/application_settings.rb b/qa/qa/runtime/application_settings.rb
index 5aeab922a12..53ed6a9266b 100644
--- a/qa/qa/runtime/application_settings.rb
+++ b/qa/qa/runtime/application_settings.rb
@@ -31,8 +31,8 @@ module QA
# TODO: This class probably needs to be refactored because this method relies on original settings to have been
# populated sometime in the past and there is no guarantee original settings instance variable is still valid
- def restore_application_settings(*application_settings_keys)
- set_application_settings(**@original_application_settings.slice(*application_settings_keys))
+ def restore_application_settings(...)
+ set_application_settings(**@original_application_settings.slice(...))
end
private
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index faf2023f7c2..f01657c8deb 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -104,7 +104,7 @@ module QA
# Specify the user-agent to allow challenges to be bypassed
# See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
- if QA::Runtime::Env.user_agent
+ unless QA::Runtime::Env.user_agent.blank?
capabilities['goog:chromeOptions'][:args] << "user-agent=#{QA::Runtime::Env.user_agent}"
end
@@ -117,6 +117,38 @@ module QA
capabilities['goog:chromeOptions'][:args] << 'window-size=1480,2200'
end
+ # Slack tries to open an external URL handler
+ # The test needs to default the handler to always open Slack
+ # to prevent a blocking popup.
+ if QA::Runtime::Env.slack_workspace
+ slack_default_preference = {
+ 'protocol_handler' => {
+ 'allowed_origin_protocol_pairs' => {
+ "https://#{QA::Runtime::Env.slack_workspace}.slack.com" => {
+ 'slack' => true
+ }
+ }
+ }
+ }
+
+ default_profile = File.join("#{chrome_profile_location}/Default")
+ FileUtils.mkdir_p(default_profile) unless Dir.exist?(default_profile)
+ preferences = slack_default_preference
+
+ # mutate the preferences if it exists
+ # else write a new file
+ if File.exist?("#{default_profile}/Preferences")
+ begin
+ preferences = JSON.parse(File.read("#{default_profile}/Preferences"))
+ preferences.deep_merge!(slack_default_preference)
+ rescue JSON::ParserError => _
+ end
+ end
+
+ File.write("#{default_profile}/Preferences", preferences.to_json)
+ append_chrome_profile_to_capabilities(capabilities)
+ end
+
when :safari
if QA::Runtime::Env.remote_mobile_device_name
capabilities['platformName'] = 'iOS'
@@ -131,10 +163,7 @@ module QA
# Use the same profile on QA runs if CHROME_REUSE_PROFILE is true.
# Useful to speed up local QA.
- if QA::Runtime::Env.reuse_chrome_profile?
- qa_profile_dir = ::File.expand_path('../../tmp/qa-profile', __dir__)
- capabilities['goog:chromeOptions'][:args] << "user-data-dir=#{qa_profile_dir}"
- end
+ append_chrome_profile_to_capabilities(capabilities) if QA::Runtime::Env.reuse_chrome_profile?
selenium_options = {
browser: QA::Runtime::Env.browser,
@@ -193,6 +222,16 @@ module QA
end
# rubocop: enable Metrics/AbcSize
+ def self.append_chrome_profile_to_capabilities(capabilities)
+ return if capabilities['goog:chromeOptions'][:args].include?(chrome_profile_location)
+
+ capabilities['goog:chromeOptions'][:args] << "user-data-dir=#{chrome_profile_location}"
+ end
+
+ def self.chrome_profile_location
+ ::File.expand_path('../../tmp/qa-profile', __dir__)
+ end
+
class Session
include Capybara::DSL
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index e952c0337f2..b53c2320537 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -293,6 +293,18 @@ module QA
ENV['JIRA_HOSTNAME']
end
+ def slack_workspace
+ ENV['QA_SLACK_WORKSPACE']
+ end
+
+ def slack_email
+ ENV['QA_SLACK_EMAIL']
+ end
+
+ def slack_password
+ ENV['QA_SLACK_PASSWORD']
+ end
+
def jenkins_admin_username
ENV.fetch('QA_JENKINS_USER', 'administrator')
end
@@ -502,6 +514,15 @@ module QA
ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir
end
+ def require_slack_env!
+ missing_env = %i[slack_workspace slack_email slack_password].select do |method|
+ ::QA::Runtime::Env.public_send(method).nil?
+ end
+ return unless missing_env.any?
+
+ raise "Missing Slack env: #{missing_env.map(&:upcase).join(', ')}"
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
index d56af9b4872..201cf7f0fcb 100644
--- a/qa/qa/runtime/fixtures.rb
+++ b/qa/qa/runtime/fixtures.rb
@@ -14,7 +14,8 @@ module QA
response = get(request.url)
unless response.code == HTTP_STATUS_OK
- raise TemplateNotFoundError, "Template at #{request.mask_url} could not be found (#{response.code}): `#{response}`."
+ raise TemplateNotFoundError,
+ "Template at #{request.mask_url} could not be found (#{response.code}): `#{response}`."
end
parse_body(response)[:content]
@@ -34,11 +35,7 @@ module QA
end
def read_fixture(fixture_path, file_name)
- file_path = Pathname
- .new(__dir__)
- .join("../fixtures/#{fixture_path}/#{file_name}")
-
- File.read(file_path)
+ File.read(File.join(Runtime::Path.fixtures_path, fixture_path, file_name))
end
private
diff --git a/qa/qa/runtime/gpg.rb b/qa/qa/runtime/gpg.rb
index 9f8baf7e580..3314b14cd68 100644
--- a/qa/qa/runtime/gpg.rb
+++ b/qa/qa/runtime/gpg.rb
@@ -3,34 +3,19 @@
module QA
module Runtime
class GPG
- attr_reader :key, :key_id
+ attr_reader :key_id
+
+ delegate :shell, to: 'QA::Service::Shellout'
def initialize
@key_id = 'B8358D73048DACC4'
- import_key(File.expand_path('qa/ee/fixtures/gpg/admin.asc'))
- @key = collect_key.first
- end
-
- private
-
- def import_key(path)
- import_key = "gpg --import #{path}"
- execute(import_key)
- end
-
- def collect_key
- get_ascii_format = "gpg --armor --export #{@key_id}"
- execute(get_ascii_format)
end
- def execute(command)
- Open3.capture2e(*command) do |stdin, out, wait|
- out.each_char { |char| print char }
+ def key
+ return @key if defined?(@key)
- if wait.value.exited? && wait.value.exitstatus.nonzero?
- raise CommandError, "Command `#{command}` failed!"
- end
- end
+ shell("gpg --import #{File.join(Path.fixtures_path, 'gpg', 'admin.asc')}")
+ @key = shell("gpg --armor --export #{key_id}")
end
end
end
diff --git a/qa/qa/scenario/test/integration/gitaly_cluster.rb b/qa/qa/scenario/test/integration/gitaly_cluster.rb
new file mode 100644
index 00000000000..66b7485a8dc
--- /dev/null
+++ b/qa/qa/scenario/test/integration/gitaly_cluster.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class GitalyCluster < Test::Instance::All
+ tags :gitaly_cluster
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/integrations.rb b/qa/qa/scenario/test/integration/integrations.rb
index b943b95c51e..0797f5d36c7 100644
--- a/qa/qa/scenario/test/instance/integrations.rb
+++ b/qa/qa/scenario/test/integration/integrations.rb
@@ -3,8 +3,8 @@
module QA
module Scenario
module Test
- module Instance
- class Integrations < All
+ module Integration
+ class Integrations < Test::Instance::All
tags :integrations
end
end
diff --git a/qa/qa/scenario/test/instance/jira.rb b/qa/qa/scenario/test/integration/jira.rb
index 784a71515cb..dda804f0fad 100644
--- a/qa/qa/scenario/test/instance/jira.rb
+++ b/qa/qa/scenario/test/integration/jira.rb
@@ -3,8 +3,8 @@
module QA
module Scenario
module Test
- module Instance
- class Jira < All
+ module Integration
+ class Jira < Test::Instance::All
tags :jira
end
end
diff --git a/qa/qa/scenario/test/integration/mtls.rb b/qa/qa/scenario/test/integration/mtls.rb
new file mode 100644
index 00000000000..6c4a035b0f2
--- /dev/null
+++ b/qa/qa/scenario/test/integration/mtls.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Mtls < Test::Instance::All
+ tags :mtls
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/cluster_provider/gcloud.rb b/qa/qa/service/cluster_provider/gcloud.rb
index 14c13eecb8d..749ebca8897 100644
--- a/qa/qa/service/cluster_provider/gcloud.rb
+++ b/qa/qa/service/cluster_provider/gcloud.rb
@@ -55,7 +55,7 @@ module QA
shell <<~CMD.tr("\n", ' ')
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 &&
chmod 700 get_helm.sh &&
- ./get_helm.sh
+ DESIRED_VERSION=v3.7.0 ./get_helm.sh
CMD
end
diff --git a/qa/qa/service/docker_run/ldap.rb b/qa/qa/service/docker_run/ldap.rb
index c33d75ff640..a6982bddf28 100644
--- a/qa/qa/service/docker_run/ldap.rb
+++ b/qa/qa/service/docker_run/ldap.rb
@@ -28,7 +28,7 @@ module QA
if volume_exists?(volume_name)
volume_name
else
- File.expand_path("../fixtures/ldap/#{volume_name}", __dir__)
+ ::File.join(Runtime::Path.fixtures_path, 'ldap', volume_name)
end
end
diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb
index 9b93297f6ff..218d5eecc85 100644
--- a/qa/qa/service/shellout.rb
+++ b/qa/qa/service/shellout.rb
@@ -13,6 +13,7 @@ module QA
def shell(command, stdin_data: nil, fail_on_exception: true, stream_progress: true, mask_secrets: []) # rubocop:disable Metrics/CyclomaticComplexity
cmd_string = Array(command).join(' ')
+ cmd_output = ''
QA::Runtime::Logger.info("Executing: `#{mask_secrets_on_string(cmd_string, mask_secrets).cyan}`")
@@ -21,7 +22,6 @@ module QA
stdin.close if stdin_data
print_progress_dots = stream_progress && !Runtime::Env.running_in_ci?
- cmd_output = ''
out.each do |line|
line = mask_secrets_on_string(line, mask_secrets)
@@ -43,6 +43,8 @@ module QA
Runtime::Logger.debug("Command output:\n#{cmd_output.strip}") unless cmd_output.empty?
end
+
+ cmd_output.strip
end
def sql_to_docker_exec_cmd(sql, username, password, database, host, container)
diff --git a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
index a10e95a860c..9017aba8e4a 100644
--- a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb
@@ -3,7 +3,10 @@
module QA
# https://github.com/gitlab-qa-github/import-test <- project under test
#
- RSpec.describe 'Manage', product_group: :import do
+ RSpec.describe 'Manage', product_group: :import, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391228',
+ type: :waiting_on
+ } do
describe 'GitHub import' do
include_context 'with github import'
diff --git a/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb
index fb530967073..8439b881ed7 100644
--- a/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb
@@ -125,11 +125,48 @@ module QA
end
end
+ context 'when hook fails' do
+ let(:fail_mock) do
+ <<~YAML
+ - request:
+ method: POST
+ path: /default
+ response:
+ status: 404
+ headers:
+ Content-Type: text/plain
+ body: 'webhook failed'
+ YAML
+ end
+
+ let(:hook_trigger_times) { 5 }
+ let(:disabled_after) { 4 }
+
+ it 'hook is auto-disabled',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/389595' do
+ setup_webhook(fail_mock, issues: true) do |webhook, smocker|
+ hook_trigger_times.times do
+ Resource::Issue.fabricate_via_api! do |issue_init|
+ issue_init.project = webhook.project
+ end
+ end
+
+ expect { smocker.history(session).size }.to eventually_eq(disabled_after)
+ .within(max_duration: 30, sleep_interval: 2),
+ -> { "Should have #{disabled_after} events, got: #{smocker.history(session).size}" }
+
+ webhook.reload!
+
+ expect(webhook.alert_status).to eql('disabled')
+ end
+ end
+ end
+
private
- def setup_webhook(**event_args)
+ def setup_webhook(mock = Vendor::Smocker::SmockerApi::DEFAULT_MOCK, **event_args)
Service::DockerRun::Smocker.init(wait: 10) do |smocker|
- smocker.register(session: session)
+ smocker.register(mock, session: session)
webhook = Resource::ProjectWebHook.fabricate_via_api! do |hook|
hook.url = smocker.url
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
index 052e3d0e32d..2dcbbadb4aa 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
@@ -78,18 +78,12 @@ module QA
end
end
- # we can't fabricate things in source instance via UI
- context "with designs", quarantine: {
- type: :broken,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366592'
- } do
+ context "with designs" do
let!(:source_design) do
- Flow::Login.sign_in(as: user)
-
- Resource::Design.fabricate_via_browser_ui! do |design|
- design.api_client = api_client
+ Resource::Design.fabricate! do |design|
+ design.api_client = source_admin_api_client
design.issue = source_issue
- end.reload!
+ end
end
let(:imported_design) do
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
index 7fe11c3bafe..42793406e6c 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb
@@ -19,11 +19,11 @@ module QA
end
let(:imported_group_member) do
- imported_group.reload!.list_members.find { |usr| usr['username'] == target_member.username }
+ imported_group.reload!.list_members.find { |usr| usr[:username] == target_member.username }
end
let(:imported_project_member) do
- imported_project.reload!.list_members.find { |usr| usr['username'] == target_member.username }
+ imported_project.reload!.list_members.find { |usr| usr[:username] == target_member.username }
end
context 'with group member' do
@@ -39,9 +39,7 @@ module QA
aggregate_failures do
expect(imported_project_member).to be_nil
- expect(imported_group_member&.fetch('access_level')).to eq(
- Resource::Members::AccessLevel::DEVELOPER
- )
+ expect(imported_group_member&.fetch(:access_level)).to eq(Resource::Members::AccessLevel::DEVELOPER)
end
end
end
@@ -59,9 +57,7 @@ module QA
aggregate_failures do
expect(imported_group_member).to be_nil
- expect(imported_project_member&.fetch('access_level')).to eq(
- Resource::Members::AccessLevel::DEVELOPER
- )
+ expect(imported_project_member&.fetch(:access_level)).to eq(Resource::Members::AccessLevel::DEVELOPER)
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
index 9f6b1d38bd7..60ece89844d 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
@@ -24,7 +24,7 @@ module QA
group.api_client = admin_api_client
group.sandbox = source_sandbox
group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- group.avatar = File.new('qa/fixtures/designs/tanuki.jpg', 'r')
+ group.avatar = File.new(File.join(Runtime::Path.fixtures_path, 'designs', 'tanuki.jpg'), 'r')
end
end
diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
index ad1abe6e5e9..124b6c9cd44 100644
--- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
@@ -5,9 +5,16 @@ module QA
describe 'User', :requires_admin, product_group: :organization do
let(:admin_api_client) { Runtime::API::Client.as_admin }
+ let!(:parent_group) do
+ QA::Resource::Group.fabricate_via_api! do |group|
+ group.path = "parent-group-to-test-user-access-#{SecureRandom.hex(8)}"
+ end
+ end
+
let!(:sub_group) do
QA::Resource::Group.fabricate_via_api! do |group|
group.path = "sub-group-to-test-user-access-#{SecureRandom.hex(8)}"
+ group.sandbox = parent_group
end
end
@@ -31,7 +38,7 @@ module QA
end
before do
- sub_group.sandbox.add_member(parent_group_user)
+ parent_group.add_member(parent_group_user)
end
it(
@@ -89,16 +96,14 @@ module QA
after do
parent_group_user.remove_via_api!
- sub_group_project.remove_via_api!
- sub_group.remove_via_api!
end
end
context 'when added to sub-group' do
let!(:parent_group_project) do
Resource::Project.fabricate_via_api! do |project|
- project.group = sub_group.sandbox
- project.name = "sub-group-project-to-test-user-access"
+ project.group = parent_group
+ project.name = "parent-group-project-to-test-user-access"
project.initialize_with_readme = true
end
end
@@ -170,8 +175,6 @@ module QA
after do
sub_group_user.remove_via_api!
- parent_group_project.remove_via_api!
- sub_group.remove_via_api!
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/group_member_access_request_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/group_member_access_request_spec.rb
new file mode 100644
index 00000000000..3a84646977f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/group_member_access_request_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage', :requires_admin, product_group: :organization do
+ describe 'Group member access request' do
+ let!(:admin_api_client) { Runtime::API::Client.as_admin }
+
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |user|
+ user.api_client = admin_api_client
+ end
+ end
+
+ let!(:group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "group-for-access-request-#{SecureRandom.hex(8)}"
+ group.api_client = admin_api_client
+ end
+ end
+
+ before do
+ Flow::Login.sign_in(as: user)
+ group.visit!
+
+ Page::Group::Show.perform(&:click_request_access)
+
+ Flow::Login.sign_in_as_admin
+
+ Page::Main::Menu.perform do |menu|
+ menu.go_to_page_by_shortcut(:todos_shortcut_button)
+ end
+
+ Page::Dashboard::Todos.perform do |todos|
+ todos.filter_todos_by_group(group)
+ end
+ end
+
+ after do
+ user&.remove_via_api!
+ end
+
+ it 'generates a todo item for the group owner',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/370132' do
+ Page::Dashboard::Todos.perform do |todos|
+ expect(todos).to have_latest_todo_with_author(
+ author: user.name,
+ action: "has requested access to group #{group.path}"
+ )
+ end
+ end
+
+ context 'when managing requests as the group owner' do
+ before do
+ Page::Dashboard::Todos.perform do |todos|
+ todos.click_todo_with_content(group.name)
+ end
+ end
+
+ context 'and request is accepted' do
+ before do
+ Page::Group::Members.perform do |members|
+ members.approve_access_request(user.username)
+ end
+ end
+
+ it 'adds user to the group',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386792' do
+ found_member = group.reload!.find_member(user.username)
+
+ expect(found_member).not_to be_nil
+ expect(found_member.fetch(:access_level))
+ .to eq(Resource::Members::AccessLevel::DEVELOPER)
+ end
+ end
+
+ context 'and request is denied' do
+ before do
+ Page::Group::Members.perform do |members|
+ members.deny_access_request(user.username)
+ end
+ end
+
+ it 'does not add user to the group',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386793' do
+ found_member = group.reload!.find_member(user.username)
+
+ expect(found_member).to be_nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
index 43a8af93e27..461928cbf1f 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', product_group: :import do
+ RSpec.describe 'Manage', product_group: :import, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391230',
+ type: :waiting_on
+ } do
describe 'GitHub import' do
include_context 'with github import'
@@ -49,9 +52,9 @@ module QA
aggregate_failures do
expect(import_page).to have_imported_project(github_repo, wait: 240)
- # validate button is present instead of navigating to avoid dealing with multiple tabs
+ # validate link 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)
+ expect(import_page).to have_go_to_project_link(github_repo)
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index e0242008785..3f5842d756e 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -5,7 +5,9 @@ module QA
it 'allows the user to register and login' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Resource::User.fabricate_via_browser_ui!
+ Resource::User.fabricate_via_browser_ui! do |user_resource|
+ user_resource.email_domain = 'gitlab.com'
+ end
Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
@@ -65,8 +67,7 @@ module QA
show.delete_account(user.password)
end
- # TODO: Remove retry_on_exception once https://gitlab.com/gitlab-org/gitlab/-/issues/24294 is resolved
- Support::Waiter.wait_until(max_duration: 120, retry_on_exception: true, sleep_interval: 3) { !user.exists? }
+ Support::Waiter.wait_until(max_duration: 120, sleep_interval: 3) { !user.exists? }
end
it 'allows recreating with same credentials', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347868' do
diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
index 9bb08cb66bc..c57900efe35 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
@@ -5,9 +5,16 @@ module QA
describe 'User', :requires_admin, product_group: :organization do
let(:admin_api_client) { Runtime::API::Client.as_admin }
+ let!(:parent_group) do
+ QA::Resource::Group.fabricate_via_api! do |group|
+ group.path = "parent-group-to-test-user-access-#{SecureRandom.hex(8)}"
+ end
+ end
+
let!(:sub_group) do
QA::Resource::Group.fabricate_via_api! do |group|
group.path = "sub-group-to-test-user-access-#{SecureRandom.hex(8)}"
+ group.sandbox = parent_group
end
end
@@ -31,7 +38,7 @@ module QA
end
before do
- sub_group.sandbox.add_member(parent_group_user)
+ parent_group.add_member(parent_group_user)
end
it(
@@ -54,16 +61,14 @@ module QA
after do
parent_group_user.remove_via_api!
- sub_group_project.remove_via_api!
- sub_group.remove_via_api!
end
end
context 'when added to sub-group' do
let!(:parent_group_project) do
Resource::Project.fabricate_via_api! do |project|
- project.group = sub_group.sandbox
- project.name = "sub-group-project-to-test-user-access"
+ project.group = parent_group
+ project.name = "parent-group-project-to-test-user-access"
project.initialize_with_readme = true
end
end
@@ -100,8 +105,6 @@ module QA
after do
sub_group_user.remove_via_api!
- parent_group_project.remove_via_api!
- sub_group.remove_via_api!
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
index eaf43f04c4b..130006fe424 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb
@@ -5,7 +5,7 @@ module QA
describe 'Design Management' do
let(:issue) { Resource::Issue.fabricate_via_api! }
let(:design_filename) { 'banana_sample.gif' }
- let(:design) { File.absolute_path(File.join('qa', 'fixtures', 'designs', design_filename)) }
+ let(:design) { File.join(Runtime::Path.fixtures_path, 'designs', design_filename) }
let(:annotation) { "This design is great!" }
before do
@@ -13,7 +13,7 @@ module QA
end
it 'user adds a design and annotates it',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347822' do
+ 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/2_plan/email/trigger_email_notification_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
index b70590e65c8..4e9d74a5117 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb
@@ -45,7 +45,7 @@ module QA
mailhog_data = JSON.parse(mailhog_response.body)
total = mailhog_data.dig('total')
subjects = mailhog_data.dig('items')
- .map(&method(:mailhog_item_subject))
+ .map { |item| mailhog_item_subject(item) }
Runtime::Logger.debug(%Q[Total number of emails: #{total}])
Runtime::Logger.debug(%Q[Subjects:\n#{subjects.join("\n")}])
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 45c3c264837..e2fd0ec9cef 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
@@ -60,7 +60,7 @@ module QA
context 'when using attachments in comments', :object_storage do
let(:png_file_name) { 'testfile.png' }
let(:file_to_attach) do
- File.absolute_path(File.join('qa', 'fixtures', 'designs', png_file_name))
+ File.join(Runtime::Path.fixtures_path, 'designs', png_file_name)
end
before do
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 829b7bab829..7b89b021d89 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
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create', :reliable, product_group: :code_review do
- context 'Add batch suggestions to a Merge Request' do
+ context 'with merge request batch suggestions' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'suggestions_project'
@@ -15,9 +15,7 @@ module QA
merge_request.title = 'Needs some suggestions'
merge_request.description = '... so please add them.'
merge_request.file_content = File.read(
- Pathname
- .new(__dir__)
- .join('../../../../../../fixtures/metrics_dashboards/templating.yml')
+ File.join(Runtime::Path.fixtures_path, 'metrics_dashboards', 'templating.yml')
)
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb
index 433ef90d9aa..499d4c00384 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- context 'Add suggestions to a Merge Request', product_group: :code_review do
+ context 'with merge request suggestions', product_group: :code_review do
let(:commit_message) { 'Applying suggested change for testing purposes.' }
let(:project) do
@@ -17,9 +17,7 @@ module QA
merge_request.title = 'Needs some suggestions'
merge_request.description = '... so please add them.'
merge_request.file_content = File.read(
- Pathname
- .new(__dir__)
- .join('../../../../../../fixtures/metrics_dashboards/templating.yml')
+ File.join(Runtime::Path.fixtures_path, 'metrics_dashboards', 'templating.yml')
)
end
end
@@ -43,7 +41,8 @@ module QA
merge_request.visit!
end
- it 'applies a single suggestion with a custom message', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347711' do
+ it 'applies a single suggestion with a custom message',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347711' do
Page::MergeRequest::Show.perform do |merge_request|
merge_request.click_diffs_tab
merge_request.apply_suggestion_with_message(commit_message)
diff --git a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb
index fbe662341c0..c782139823e 100644
--- a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_file_upload_spec.rb
@@ -17,7 +17,7 @@ module QA
end
it 'by creating a formatted page with an image uploaded',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347640' do
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347640' do
initial_wiki.visit!
Page::Project::Wiki::Show.perform(&:click_new_page)
@@ -26,7 +26,7 @@ module QA
edit.set_title(page_title)
edit.use_new_editor
edit.add_heading('Heading 1', heading_text)
- edit.upload_image(File.absolute_path(File.join('qa', 'fixtures', 'designs', image_file_name)))
+ edit.upload_image(File.join(Runtime::Path.fixtures_path, 'designs', image_file_name))
end
Page::Project::Wiki::Edit.perform(&:click_submit)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
index 50df8afafaf..86a9f2c46fb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
@@ -11,7 +11,7 @@ module QA
shared_examples 'project license detection' do
it 'displays the name of the license on the repository' do
- license_path = File.expand_path("../../../../../fixtures/software_licenses/#{license_file_name}", __dir__)
+ license_path = File.join(Runtime::Path.fixtures_path, 'software_licenses', license_file_name)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.add_files([{ file_path: 'LICENSE', content: File.read(license_path) }])
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
index 8c171e6bc8c..080832990c9 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb
@@ -2,14 +2,14 @@
module QA
RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' },
- feature_flag: { name: 'vscode_web_ide', scope: :global },
- product_group: :editor,
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387928',
- type: :stale
- } do
+ feature_flag: { name: 'vscode_web_ide', scope: :global },
+ product_group: :editor,
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387928',
+ type: :stale
+ } do
describe 'Git Server Hooks' do
- let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', 'README.md')) }
+ let(:file_path) { File.join(Runtime::Path.fixtures_path, 'web_ide', 'README.md') }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -29,9 +29,9 @@ module QA
Runtime::Feature.enable(:vscode_web_ide)
end
- context 'Custom error messages' do
+ context 'with custom error messages' do
it 'renders preconfigured error message when user hook failed on commit in WebIDE',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/364751' do
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/364751' do
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
Page::Project::WebIDE::Edit.perform do |ide|
ide.wait_until_ide_loads
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
index 781623d10f6..b83a95694de 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb
@@ -1,12 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387032',
- type: :stale
- } do
+ RSpec.describe 'Create', product_group: :editor,
+ feature_flag: { name: 'vscode_web_ide', scope: :global },
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387032', type: :stale } do
describe 'Upload a file in Web IDE' do
- let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', file_name)) }
+ let(:file_path) { File.join(Runtime::Path.fixtures_path, 'web_ide', file_name) }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -43,7 +42,8 @@ module QA
context 'when the file is a text file' do
let(:file_name) { 'text_file.txt' }
- it 'shows the Edit tab with the text', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347852' do
+ it 'shows the Edit tab with the text',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347852' do
Page::Project::WebIDE::Edit.perform do |ide|
ide.wait_until_ide_loads
ide.upload_file(file_path)
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
deleted file mode 100644
index 599c34ed3db..00000000000
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe(
- 'Create',
- :runner,
- # TODO: remove limitation to only run on main when the bug is fixed
- only: { pipeline: :main },
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338179',
- type: :bug
- },
- feature_flag: { name: 'vscode_web_ide', scope: :global },
- product_group: :editor
- ) do
- describe 'Web IDE web terminal' do
- before do
- Runtime::Feature.disable(:vscode_web_ide)
- project = Resource::Project.fabricate_via_api! do |project|
- project.name = 'web-terminal-project'
- end
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'Add .gitlab/.gitlab-webide.yml'
- commit.add_files(
- [
- {
- file_path: '.gitlab/.gitlab-webide.yml',
- content: <<~YAML
- terminal:
- tags: ["web-ide"]
- script: sleep 60
- YAML
- }
- ]
- )
- end
-
- @runner = Resource::ProjectRunner.fabricate_via_api! do |runner|
- runner.project = project
- runner.name = "qa-runner-#{Time.now.to_i}"
- runner.tags = %w[web-ide]
- runner.image = 'gitlab/gitlab-runner:latest'
- runner.config = <<~END
- concurrent = 1
-
- [session_server]
- listen_address = "0.0.0.0:8093"
- advertise_address = "localhost:8093"
- session_timeout = 120
- END
- end
-
- Flow::Login.sign_in
-
- project.visit!
- end
-
- after do
- Runtime::Feature.enable(:vscode_web_ide)
- @runner.remove_via_api! if @runner
- end
-
- it 'user starts the web terminal', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347737' do
- Page::Project::Show.perform(&:open_web_ide!)
-
- # Start the web terminal and check that there were no errors
- # The terminal screen is a canvas element, so we can't read its content,
- # so we infer that it's working if:
- # a) The terminal JS package has loaded, and
- # b) It's not stuck in a "Loading/Starting" state, and
- # c) There's no alert stating there was a problem
- # d) There are no JS console errors
- #
- # The terminal itself is a third-party package so we assume it is
- # adequately tested elsewhere.
- #
- # There are also FE specs
- # * spec/frontend/ide/components/terminal/terminal_controls_spec.js
- Page::Project::WebIDE::Edit.perform do |edit|
- edit.wait_until_ide_loads
- edit.start_web_terminal
-
- expect(edit).to have_no_alert
- expect(edit).to have_finished_loading
- expect(edit).to have_terminal_screen
- end
-
- # It takes a few seconds for console errors to appear
- sleep 3
-
- errors = page.driver.browser.logs.get(:browser)
- .select { |e| e.level == "SEVERE" }
- .to_a
-
- if errors.present?
- raise("Console error(s):\n#{errors.join("\n\n")}")
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb
index ddd91f97515..379499662c2 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify', :runner, product_group: :pipeline_execution do
- context 'When pipeline is blocked' do
+ context 'when pipeline is blocked' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb
deleted file mode 100644
index 5dff2db6f2b..00000000000
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
- context 'when job is configured to only run on merge_request_events' do
- let(:mr_only_job_name) { 'mr_only_job' }
- let(:non_mr_only_job_name) { 'non_mr_only_job' }
- let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
-
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'merge-request-only-job'
- end
- end
-
- let!(:runner) do
- Resource::ProjectRunner.fabricate! do |runner|
- runner.project = project
- runner.name = executor
- runner.tags = [executor]
- end
- end
-
- let!(:ci_file) 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
- #{mr_only_job_name}:
- tags: ["#{executor}"]
- script: echo 'OK'
- rules:
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- #{non_mr_only_job_name}:
- tags: ["#{executor}"]
- script: echo 'OK'
- rules:
- - if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
- YAML
- }
- ]
- )
- end
- end
-
- let(:merge_request) do
- Resource::MergeRequest.fabricate_via_api! do |merge_request|
- merge_request.project = project
- merge_request.description = Faker::Lorem.sentence
- merge_request.target_new_branch = false
- merge_request.file_name = 'new.txt'
- merge_request.file_content = Faker::Lorem.sentence
- end
- end
-
- before do
- Flow::Login.sign_in
- # TODO: We should remove (wait) revisiting logic when
- # https://gitlab.com/gitlab-org/gitlab/-/issues/385332 is resolved
- Support::Waiter.wait_until do
- merge_request.visit!
- Page::MergeRequest::Show.perform(&:click_pipeline_link)
- Page::Project::Pipeline::Show.perform(&:has_merge_request_badge_tag?)
- end
- end
-
- after do
- runner.remove_via_api!
- end
-
- it 'only runs the job configured to run on merge requests', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347662' do
- Page::Project::Pipeline::Show.perform do |pipeline|
- aggregate_failures do
- expect(pipeline).to have_job(mr_only_job_name)
- expect(pipeline).to have_no_job(non_mr_only_job_name)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb
index 4294235a8b1..37d1e20111d 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb
@@ -29,6 +29,9 @@ module QA
{
file_path: '.gitlab-ci.yml',
content: <<~YAML
+ default:
+ tags: ["#{executor}"]
+
stages:
- Stage1
- Stage2
@@ -36,26 +39,22 @@ module QA
Prep:
stage: Stage1
- tags: ["#{executor}"]
script: exit 0
when: manual
Build:
stage: Stage2
- tags: ["#{executor}"]
needs: ['Prep']
script: exit 0
parallel: 6
Test:
stage: Stage3
- tags: ["#{executor}"]
needs: ['Build']
script: exit 0
Deploy:
stage: Stage3
- tags: ["#{executor}"]
needs: ['Test']
script: exit 0
parallel: 6
@@ -67,6 +66,8 @@ module QA
end
before do
+ make_sure_to_have_a_skipped_pipeline
+
Flow::Login.sign_in
project.visit!
Flow::Pipeline.visit_latest_pipeline(status: 'skipped')
@@ -84,7 +85,7 @@ module QA
show.click_job_action('Prep') # Trigger pipeline manually
show.wait_until(max_duration: 300, sleep_interval: 2, reload: false) do
- project.pipelines.last[:status] == 'success'
+ project.latest_pipeline[:status] == 'success'
end
aggregate_failures do
@@ -99,6 +100,50 @@ module QA
end
end
end
+
+ private
+
+ # Wait for first pipeline to finish and have "skipped" status
+ # If it takes too long, create new pipeline and retry (2 times)
+ def make_sure_to_have_a_skipped_pipeline
+ attempts ||= 1
+ Runtime::Logger.info('Waiting for pipeline to have status "skipped"...')
+ Support::Waiter.wait_until(max_duration: 120, sleep_interval: 3, retry_on_exception: true) do
+ project.latest_pipeline[:status] == 'skipped'
+ end
+ rescue Support::Repeater::WaitExceededError
+ raise 'Failed to create skipped pipeline after 3 attempts.' unless (attempts += 1) < 4
+
+ Runtime::Logger.debug(
+ "Previous pipeline took too long to finish. Potential jobs with problems:\n#{problematic_jobs}"
+ )
+ Runtime::Logger.info("Triggering a new pipeline...")
+ trigger_new_pipeline
+ retry
+ end
+
+ def trigger_new_pipeline
+ original_count = project.pipelines.length
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = project
+ end
+
+ Support::Waiter.wait_until(sleep_interval: 1) { project.pipelines.length > original_count }
+ end
+
+ # We know that all the jobs in pipeline are purposely skipped
+ # The pipeline should have status "skipped" almost right away after being created
+ # If pipeline is held up, likely because there are some jobs that
+ # doesn't have either "skipped" or "manual" status
+ def problematic_jobs
+ pipeline = Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = project
+ pipeline.id = project.latest_pipeline[:id]
+ end
+
+ acceptable_statuses = %w[skipped manual]
+ pipeline.pipeline_jobs.select { |job| !(acceptable_statuses.include? job[:status]) }
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb
new file mode 100644
index 00000000000..fba2f22b3e5
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner, product_group: :runner do
+ describe 'Group runner registration' do
+ let(:executor) { "qa-runner-#{Time.now.to_i}" }
+
+ let!(:runner) do
+ Resource::GroupRunner.fabricate! do |runner|
+ runner.name = executor
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it(
+ 'user registers a new group runner',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/388740'
+ ) do
+ Flow::Login.sign_in
+
+ runner.group.visit!
+
+ Page::Group::Menu.perform(&:go_to_runners)
+
+ expect(page).to have_content(executor)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
index e141bac5e3f..4db935df43d 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -15,7 +15,7 @@ module QA
runner.remove_via_api!
end
- it 'user registers a new specific runner', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348025' do
+ it 'user registers a new project runner', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348025' do
Flow::Login.sign_in
runner.project.visit!
diff --git a/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb b/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb
index 800324afc58..ec07116550f 100644
--- a/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/container_registry/container_registry_omnibus_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :skip_live_env, product_group: :container_registry do
+ RSpec.describe 'Package', :orchestrated, :skip_live_env, product_group: :container_registry, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/390090',
+ type: :investigating
+ } do
describe 'Self-managed Container Registry' do
include Support::Helpers::MaskToken
@@ -136,26 +139,30 @@ module QA
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files([{
- file_path: '.gitlab-ci.yml',
- content:
- <<~YAML
- build:
- image: "#{docker_client_version}"
- stage: build
- services:
- - name: "#{docker_client_version}-dind"
- command: ["--insecure-registry=gitlab.test:5050"]
- variables:
- IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
- script:
- - docker login -u #{auth_user} -p #{auth_token} gitlab.test:5050
- - docker build -t $IMAGE_TAG .
- - docker push $IMAGE_TAG
- tags:
- - "runner-for-#{project.name}"
- YAML
- }])
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ build:
+ image: "#{docker_client_version}"
+ stage: build
+ services:
+ - name: "#{docker_client_version}-dind"
+ command: ["--insecure-registry=gitlab.test:5050"]
+ variables:
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+ script:
+ - docker login -u #{auth_user} -p #{auth_token} gitlab.test:5050
+ - docker build -t $IMAGE_TAG .
+ - docker push $IMAGE_TAG
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ }
+ ]
+ )
end
end
@@ -181,39 +188,47 @@ module QA
end
end
- context "when tls is enabled" do
- it "pushes image and deletes tag", :registry_tls, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347591' do
+ context 'when tls is enabled' do
+ it(
+ 'pushes image and deletes tag',
+ :registry_tls,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347591'
+ ) do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'Add .gitlab-ci.yml'
- commit.add_files([{
- file_path: '.gitlab-ci.yml',
- content:
- <<~YAML
- build:
- image: docker:19.03.12
- stage: build
- services:
- - name: docker:19.03.12-dind
- command:
- - /bin/sh
- - -c
- - |
- apk add --no-cache openssl
- true | openssl s_client -showcerts -connect gitlab.test:5050 > /usr/local/share/ca-certificates/gitlab.test.crt
- update-ca-certificates
- dockerd-entrypoint.sh || exit
- variables:
- IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
- script:
- - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD gitlab.test:5050
- - docker build -t $IMAGE_TAG .
- - docker push $IMAGE_TAG
- tags:
- - "runner-for-#{project.name}"
- YAML
- }])
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ build:
+ image: docker:19.03.12
+ stage: build
+ services:
+ - name: docker:19.03.12-dind
+ command:
+ - /bin/sh
+ - -c
+ - |
+ apk add --no-cache openssl
+ true | openssl s_client -showcerts -connect gitlab.test:5050 > /usr/local/share/ca-certificates/gitlab.test.crt
+ update-ca-certificates
+ dockerd-entrypoint.sh || exit
+ variables:
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+ script:
+ - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD gitlab.test:5050
+ - docker build -t $IMAGE_TAG .
+ - docker push $IMAGE_TAG
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ }
+ ]
+ )
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb
index 4c15b7c7f99..f95bcc59db1 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb
@@ -43,7 +43,10 @@ module QA
end
end
- it "pushes and pulls a helm chart", testcase: params[:testcase] do
+ it "pushes and pulls a helm chart", testcase: params[:testcase], quarantine: {
+ type: :stale,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391649'
+ } do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
helm_upload_yaml = ERB.new(read_fixture('package_managers/helm', 'helm_upload_package.yaml.erb')).result(binding)
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 cda45efd828..3fb5c921187 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
@@ -41,7 +41,11 @@ module QA
'using a ci job token' => {
authentication_token_type: :ci_job_token,
maven_header_name: 'Job-Token',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579',
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373189',
+ type: :stale
+ }
}
}
end
@@ -60,7 +64,7 @@ module QA
end
end
- it 'pushes and pulls a maven package', testcase: params[:testcase] do
+ it 'pushes and pulls a maven package', testcase: params[:testcase], quarantine: params[:quarantine] do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding)
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb
index a9d66c93fac..d86ce09c4e1 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb
@@ -31,7 +31,10 @@ module QA
end
end
- it 'pushes and pulls a maven package via gradle', testcase: params[:testcase] do
+ it 'pushes and pulls a maven package via gradle', testcase: params[:testcase], quarantine: {
+ type: :stale,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391650'
+ } do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven/gradle', 'gradle_upload_package.yaml.erb')).result(binding)
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 0e1ac4d861d..c2cbec3fbb7 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
@@ -97,7 +97,10 @@ module QA
end
end
- it 'publishes a nuget package at the project endpoint and installs it from the group endpoint', testcase: params[:testcase] do
+ it 'publishes a nuget package at the project endpoint and installs it from the group endpoint', testcase: params[:testcase], quarantine: {
+ type: :stale,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391648'
+ } do
Flow::Login.sign_in
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
@@ -133,14 +136,14 @@ module QA
{
file_path: 'otherdotnet.csproj',
content: <<~EOF
- <Project Sdk="Microsoft.NET.Sdk">
+ <Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>net7.0</TargetFramework>
- </PropertyGroup>
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net7.0</TargetFramework>
+ </PropertyGroup>
- </Project>
+ </Project>
EOF
}
]
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb
index 2b7dd8fb673..b44020ddfce 100644
--- a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb
+++ b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Monitor', product_group: :respond do
+ RSpec.describe 'Monitor', :smoke, product_group: :respond do
describe 'Alert settings' do
shared_examples 'sends test alert' do
it 'creates new alert' do
@@ -24,6 +24,7 @@ module QA
before do
Flow::Login.sign_in
project.visit!
+ Flow::AlertSettings.go_to_monitor_settings
end
context(
@@ -35,7 +36,8 @@ module QA
end
before do
- Flow::AlertSettings.setup_http_endpoint(payload: payload)
+ Flow::AlertSettings.setup_http_endpoint_integration
+ Flow::AlertSettings.send_test_alert(payload: payload)
end
it_behaves_like 'sends test alert'
@@ -73,7 +75,8 @@ module QA
end
before do
- Flow::AlertSettings.setup_prometheus(payload: payload)
+ Flow::AlertSettings.setup_prometheus_integration
+ Flow::AlertSettings.send_test_alert(payload: payload)
end
it_behaves_like 'sends test alert'
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb
new file mode 100644
index 00000000000..565f56b90ec
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Monitor', product_group: :respond do
+ describe 'Alert' do
+ shared_examples 'new alert' do
+ it 'automatically creates new incident' do
+ Page::Project::Menu.perform(&:go_to_monitor_incidents)
+ Page::Project::Monitor::Incidents::Index.perform do |index|
+ expect(index).to have_incident
+ end
+ end
+ end
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-for-alerts'
+ project.description = 'Project for alerts'
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ Flow::AlertSettings.go_to_monitor_settings
+ Flow::AlertSettings.enable_create_incident
+ end
+
+ context(
+ 'when using HTTP endpoint integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/388469'
+ ) do
+ before do
+ Flow::AlertSettings.setup_http_endpoint_integration
+ Flow::AlertSettings.send_test_alert
+ end
+
+ it_behaves_like 'new alert'
+ end
+
+ context(
+ 'when using Prometheus integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390123'
+ ) do
+ before do
+ Flow::AlertSettings.setup_prometheus_integration
+ Flow::AlertSettings.send_test_alert(integration_type: 'prometheus')
+ end
+
+ it_behaves_like 'new alert'
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb
index 9d4aff59e48..96db10c1683 100644
--- a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Monitor', product_group: :respond do
+ RSpec.describe 'Monitor', :smoke, product_group: :respond do
describe 'Alert settings' do
shared_examples 'sends test alert using authorization key' do |type|
it 'creates new alert', :aggregate_failures do
@@ -22,7 +22,8 @@ module QA
Page::Project::Menu.perform(&:go_to_monitor_alerts)
Page::Project::Monitor::Alerts::Index.perform do |index|
- expect(index).to have_alert_with_title(alert_title)
+ expect { index.has_alert_with_title?(alert_title) }
+ .to eventually_be_truthy.within(max_duration: 60, reload_page: index)
end
end
end
@@ -36,9 +37,14 @@ module QA
let(:alert_title) { Faker::Lorem.word }
+ let(:credentials) do
+ Flow::AlertSettings.integration_credentials
+ end
+
before do
Flow::Login.sign_in
project.visit!
+ Flow::AlertSettings.go_to_monitor_settings
end
context(
@@ -49,8 +55,8 @@ module QA
{ title: alert_title, description: alert_title }
end
- let(:credentials) do
- Flow::AlertSettings.setup_http_endpoint(send: false)
+ before do
+ Flow::AlertSettings.setup_http_endpoint_integration
end
it_behaves_like 'sends test alert using authorization key', 'http'
@@ -87,8 +93,8 @@ module QA
}
end
- let(:credentials) do
- Flow::AlertSettings.setup_prometheus(send: false)
+ before do
+ Flow::AlertSettings.setup_prometheus_integration
end
it_behaves_like 'sends test alert using authorization key'
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb
new file mode 100644
index 00000000000..70874e46f27
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/email_notification_for_alert_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Monitor', :orchestrated, :smtp, :requires_admin, product_group: :respond do
+ describe 'Alert' do
+ shared_examples 'notification on new alert', :aggregate_failures do
+ it 'sends email to user' do
+ expect { email_subjects }.to eventually_include(alert_email_subject).within(max_duration: 60)
+ expect(recipient_email_addresses).to include(user.email)
+ end
+ end
+
+ let!(:admin_api_client) { Runtime::API::Client.as_admin }
+
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |user|
+ user.api_client = admin_api_client
+ user.hard_delete_on_api_removal = true
+ end
+ end
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-for-alerts'
+ project.description = 'Project for alerts'
+ end
+ end
+
+ let(:alert_title) { Faker::Lorem.word }
+ let(:mail_hog_api) { Vendor::MailHog::API.new }
+ let(:alert_email_subject) { "#{project.name} | Alert: #{alert_title}" }
+ let(:http_payload) { { title: alert_title, description: alert_title } }
+
+ let(:prometheus_payload) do
+ {
+ version: '4',
+ groupKey: nil,
+ status: 'firing',
+ receiver: '',
+ groupLabels: {},
+ commonLabels: {},
+ commonAnnotations: {},
+ externalURL: '',
+ alerts: [
+ {
+ startsAt: Time.now,
+ generatorURL: Faker::Internet.url,
+ endsAt: nil,
+ status: 'firing',
+ labels: { gitlab_environment_name: Faker::Lorem.word },
+ annotations:
+ {
+ title: alert_title,
+ gitlab_y_label: 'status'
+ }
+ }
+ ]
+ }
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ Flow::AlertSettings.go_to_monitor_settings
+ Flow::AlertSettings.enable_email_notification
+ end
+
+ context 'when user is a maintainer' do
+ before do
+ project.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ end
+
+ context(
+ 'when using HTTP endpoint integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/389993'
+ ) do
+ before do
+ send_http_alert
+ end
+
+ it_behaves_like 'notification on new alert'
+ end
+
+ context(
+ 'when using Prometheus integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/389994'
+ ) do
+ before do
+ send_prometheus_alert
+ end
+
+ it_behaves_like 'notification on new alert'
+ end
+ end
+
+ context 'when user is an owner' do
+ before do
+ project.add_member(user, Resource::Members::AccessLevel::OWNER)
+ end
+
+ context(
+ 'when using HTTP endpoint integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390145'
+ ) do
+ before do
+ send_http_alert
+ end
+
+ it_behaves_like 'notification on new alert'
+ end
+
+ context(
+ 'when using Prometheus integration',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/390144'
+ ) do
+ before do
+ send_prometheus_alert
+ end
+
+ it_behaves_like 'notification on new alert'
+ end
+ end
+
+ private
+
+ def send_http_alert
+ Flow::AlertSettings.setup_http_endpoint_integration
+ Flow::AlertSettings.send_test_alert(payload: http_payload)
+ end
+
+ def send_prometheus_alert
+ Flow::AlertSettings.setup_prometheus_integration
+ Flow::AlertSettings.send_test_alert(payload: prometheus_payload)
+ end
+
+ def mail_hog_messages
+ mail_hog_api.fetch_messages
+ end
+
+ def email_subjects
+ mail_hog_messages.map(&:subject)
+ end
+
+ def recipient_email_addresses
+ mail_hog_messages.map(&:to)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb b/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
index c6836d11803..805e5ce782a 100644
--- a/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
@@ -8,18 +8,25 @@ module QA
QA::EE::Resource::Settings::Elasticsearch.fabricate_via_api! unless advanced_search_on
end
- after do
- Runtime::Search.disable_elasticsearch(api_client) if !advanced_search_on && !api_client.nil?
- end
+ # TODO: convert check_advanced_search_status method to use the API instead of the UI once the functionality exists
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/382849 and then we can resume turning off advanced search after the
+ # tests as in the `after` block here. For now the advanced search tests will have the side effect of turning on
+ # advanced search if it wasn't enabled before the tests run.
+
+ # after do
+ # Runtime::Search.disable_elasticsearch(api_client) if !advanced_search_on && !api_client.nil?
+ # end
- # TODO: convert this method to use the API instead of the UI once the functionality exists
- # https://gitlab.com/gitlab-org/gitlab/-/issues/382849
def check_advanced_search_status
Flow::Login.sign_in
- QA::Page::Main::Menu.perform do |menu|
- menu.search_for('lorem ipsum')
+ QA::Support::Retrier.retry_on_exception(
+ max_attempts: Runtime::Search::RETRY_MAX_ITERATION,
+ sleep_interval: Runtime::Search::RETRY_SLEEP_INTERVAL) do
+ QA::Page::Main::Menu.perform do |menu|
+ menu.search_for('lorem ipsum')
+ end
+ page.has_text?('Advanced search is enabled')
end
- page.has_text?('Advanced search is enabled')
end
end
end
diff --git a/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb b/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
index 853f427db12..4bd81ccdf36 100644
--- a/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
+++ b/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
@@ -32,7 +32,7 @@ module QA
Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = source_admin_api_client
group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- group.avatar = File.new("qa/fixtures/designs/tanuki.jpg", "r")
+ group.avatar = File.new(File.join(Runtime::Path.fixtures_path, 'designs', 'tanuki.jpg'), "r")
end
end
diff --git a/qa/qa/specs/knapsack_runner.rb b/qa/qa/specs/knapsack_runner.rb
index 4908553e43d..0c4f938ee28 100644
--- a/qa/qa/specs/knapsack_runner.rb
+++ b/qa/qa/specs/knapsack_runner.rb
@@ -4,6 +4,8 @@ module QA
module Specs
class KnapsackRunner
def self.run(args)
+ QA::Support::KnapsackReport.configure!
+
allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
Knapsack.logger.info '==== Knapsack specs to execute ====='
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
index 0e6e3973de9..1bf189ed6ac 100644
--- a/qa/qa/specs/spec_helper.rb
+++ b/qa/qa/specs/spec_helper.rb
@@ -11,6 +11,7 @@ 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)
+QA::Support::KnapsackReport.configure!
# Enable zero monkey patching mode before loading any other RSpec code.
RSpec.configure(&:disable_monkey_patching!)
diff --git a/qa/qa/support/matchers/eventually_matcher.rb b/qa/qa/support/matchers/eventually_matcher.rb
index 3f451f89246..3fdc5b711b0 100644
--- a/qa/qa/support/matchers/eventually_matcher.rb
+++ b/qa/qa/support/matchers/eventually_matcher.rb
@@ -22,6 +22,7 @@ module QA
be
include
match
+ have_content
be_truthy
be_falsey
be_empty
@@ -140,9 +141,9 @@ module QA
# @param [Boolean] negate
# @return [String]
def fail_message(negate: false)
- "#{e}:\n\nexpected #{negate ? 'not ' : ''}to #{description}\n\n"\
- "last attempt was: #{@result.nil? ? 'nil' : actual_formatted}\n\n"\
- "Diff:#{diff}"
+ "#{e}:\n\nexpected #{negate ? 'not ' : ''}to #{description}\n\n" \
+ "last attempt was: #{@result.nil? ? 'nil' : actual_formatted}\n\n" \
+ "Diff:#{diff}"
end
# Formatted expect
diff --git a/qa/qa/tools/ci/test_results.rb b/qa/qa/tools/ci/test_results.rb
deleted file mode 100644
index 635b69f6ca0..00000000000
--- a/qa/qa/tools/ci/test_results.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Tools
- module Ci
- class TestResults
- include Helpers
-
- def initialize(pipeline_name, test_report_job_name, report_path)
- @pipeline_name = pipeline_name
- @test_report_job_name = test_report_job_name
- @report_path = report_path
- end
-
- # Get test report artifacts from downstream pipeline
- #
- # @param [String] pipeline_name
- # @param [String] test_report_job_name
- # @param [String] report_path
- # @return [void]
- def self.get(pipeline_name, test_report_job_name, report_path)
- new(pipeline_name, test_report_job_name, report_path).download_test_results
- end
-
- # Download test results from child pipeline
- #
- # @return [void]
- def download_test_results
- logger.info("Fetching test results for '#{pipeline_name}'")
-
- logger.debug(" fetching pipeline id of '#{pipeline_name}' child pipeline")
- downstream_pipeline_id = api_get("#{pipelines_url(pipeline_id)}/bridges")
- .find { |bridge| bridge[:name] == pipeline_name }
- &.dig(:downstream_pipeline, :id)
- return logger.error("Child pipeline '#{pipeline_name}' not found!") unless downstream_pipeline_id
-
- logger.debug(" fetching job id of test report job")
- job_id = api_get("#{pipelines_url(downstream_pipeline_id)}/jobs")
- .find { |job| job[:name] == test_report_job_name }
- &.fetch(:id)
- return logger.error("Test report job '#{test_report_job_name}' not found!") unless job_id
-
- logger.debug(" fetching test results artifact archive")
- response = api_get("/projects/#{project_id}/jobs/#{job_id}/artifacts", raw_response: true)
-
- logger.info("Extracting test result archive")
- system("unzip", "-o", "-d", report_path, response.file.path)
- end
-
- private
-
- attr_reader :pipeline_name, :test_report_job_name, :report_path
-
- # Base get pipeline url
- #
- # @param [Integer] id
- # @return [String]
- def pipelines_url(id)
- "/projects/#{project_id}/pipelines/#{id}"
- end
-
- # Current pipeline id
- #
- # @return [String]
- def pipeline_id
- ENV["CI_PIPELINE_ID"]
- end
-
- # Current project id
- #
- # @return [String]
- def project_id
- ENV["CI_PROJECT_ID"]
- end
- end
- end
- end
-end
diff --git a/qa/spec/resource/api_fabricator_spec.rb b/qa/spec/resource/api_fabricator_spec.rb
index 0cec6b2a1ea..337c6772a06 100644
--- a/qa/spec/resource/api_fabricator_spec.rb
+++ b/qa/spec/resource/api_fabricator_spec.rb
@@ -90,12 +90,12 @@ RSpec.describe QA::Resource::ApiFabricator do
context 'when creating a resource' do
before do
- allow(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ allow(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(raw_post)
end
it 'returns the resource URL' do
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
- expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(raw_post)
expect(subject.fabricate_via_api!).to eq(resource_web_url)
end
@@ -112,7 +112,7 @@ RSpec.describe QA::Resource::ApiFabricator do
it 'raises a ResourceFabricationFailedError exception' do
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
- expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(raw_post)
expect { subject.fabricate_via_api! }.to raise_error do |error|
expect(error.class).to eql(described_class::ResourceFabricationFailedError)
@@ -128,7 +128,7 @@ RSpec.describe QA::Resource::ApiFabricator do
allow(QA::Support::Loglinking).to receive(:logging_environment).and_return(nil)
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
- expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(response)
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(response)
expect { subject.fabricate_via_api! }.to raise_error do |error|
expect(error.class).to eql(described_class::ResourceFabricationFailedError)
@@ -149,7 +149,7 @@ RSpec.describe QA::Resource::ApiFabricator do
allow(Time).to receive(:now).and_return(time)
expect(api_request).to receive(:new).with(api_client_instance, subject.api_post_path).and_return(double(url: resource_web_url))
- expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(response)
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(response)
expect { subject.fabricate_via_api! }.to raise_error do |error|
expect(error.class).to eql(described_class::ResourceFabricationFailedError)
@@ -195,7 +195,7 @@ RSpec.describe QA::Resource::ApiFabricator do
let(:transformed_resource) { { existing: 'foo', new: 'foobar', web_url: resource_web_url } }
it 'transforms the resource' do
- expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body).and_return(raw_post)
+ expect(subject).to receive(:post).with(resource_web_url, subject.api_post_body, {}).and_return(raw_post)
expect(subject).to receive(:transform_api_resource).with(response).and_return(transformed_resource)
subject.fabricate_via_api!
diff --git a/qa/spec/runtime/script_extensions/interceptor_spec.rb b/qa/spec/runtime/script_extensions/interceptor_spec.rb
index 28e8007973c..d7835b28071 100644
--- a/qa/spec/runtime/script_extensions/interceptor_spec.rb
+++ b/qa/spec/runtime/script_extensions/interceptor_spec.rb
@@ -3,7 +3,7 @@
RSpec.describe 'Interceptor' do
let(:browser) { Capybara.current_session }
# need a real host for the js runtime
- let(:url) { "file://#{__dir__}/../../../qa/fixtures/script_extensions/test.html" }
+ let(:url) { "file://#{File.join(Runtime::Path.fixtures_path, 'script_extensions', 'test.html')}" }
before(:context) do
skip 'Only can test for chrome' unless QA::Runtime::Env.can_intercept?
diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake
index 84a26e3e555..aaf691de1b5 100644
--- a/qa/tasks/ci.rake
+++ b/qa/tasks/ci.rake
@@ -54,15 +54,15 @@ namespace :ci do
append_to_file(env_file, "QA_FEATURE_FLAGS='#{feature_flags}'")
end
- desc "Download test results from downstream pipeline"
- task :download_test_results, [:trigger_name, :test_report_job_name, :report_path] do |_, args|
- QA::Tools::Ci::TestResults.get(args[:trigger_name], args[:test_report_job_name], args[:report_path])
- end
-
desc "Export test run metrics to influxdb"
task :export_test_metrics, [:glob] do |_, args|
raise("Metrics file glob pattern is required") unless args[:glob]
QA::Tools::Ci::TestMetrics.export(args[:glob])
end
+
+ desc "Get available QA environment variables"
+ task :env_var_name_list do
+ puts Gitlab::QA::Runtime::Env.variables.keys.join("\n")
+ end
end
diff --git a/qa/tasks/knapsack.rake b/qa/tasks/knapsack.rake
index c502d1cbb4a..5e60703ced3 100644
--- a/qa/tasks/knapsack.rake
+++ b/qa/tasks/knapsack.rake
@@ -20,6 +20,7 @@ namespace :knapsack do
test_stage_name = args[:stage_name]
knapsack_reports = ENV["QA_KNAPSACK_REPORTS"]&.split(",")
ci_token = ENV["QA_GITLAB_CI_TOKEN"]
+ QA::Support::KnapsackReport.configure!
reports = if knapsack_reports
knapsack_reports
@@ -43,6 +44,7 @@ namespace :knapsack do
desc "Merge and upload knapsack report"
task :upload, [:glob] do |_task, args|
+ QA::Support::KnapsackReport.configure!
QA::Support::KnapsackReport.upload_report(args[:glob])
end