summaryrefslogtreecommitdiff
path: root/qa/qa
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 14:22:11 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 14:22:11 +0000
commit0c872e02b2c822e3397515ec324051ff540f0cd5 (patch)
treece2fb6ce7030e4dad0f4118d21ab6453e5938cdd /qa/qa
parentf7e05a6853b12f02911494c4b3fe53d9540d74fc (diff)
downloadgitlab-ce-15.7.0-rc42.tar.gz
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'qa/qa')
-rw-r--r--qa/qa/ce/strategy.rb7
-rw-r--r--qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb17
-rw-r--r--qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb2
-rw-r--r--qa/qa/flow/alert_settings.rb32
-rw-r--r--qa/qa/flow/login.rb6
-rw-r--r--qa/qa/git/location.rb5
-rw-r--r--qa/qa/page/admin/menu.rb12
-rw-r--r--qa/qa/page/base.rb39
-rw-r--r--qa/qa/page/component/blob_content.rb6
-rw-r--r--qa/qa/page/component/custom_metric.rb49
-rw-r--r--qa/qa/page/component/dropdown.rb110
-rw-r--r--qa/qa/page/component/invite_members_modal.rb5
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb8
-rw-r--r--qa/qa/page/component/snippet.rb2
-rw-r--r--qa/qa/page/file/edit.rb4
-rw-r--r--qa/qa/page/group/settings/general.rb40
-rw-r--r--qa/qa/page/group/settings/group_deploy_tokens.rb6
-rw-r--r--qa/qa/page/group/show.rb2
-rw-r--r--qa/qa/page/main/login.rb4
-rw-r--r--qa/qa/page/main/menu.rb2
-rw-r--r--qa/qa/page/merge_request/new.rb2
-rw-r--r--qa/qa/page/merge_request/show.rb11
-rw-r--r--qa/qa/page/profile/two_factor_auth.rb3
-rw-r--r--qa/qa/page/project/import/github.rb14
-rw-r--r--qa/qa/page/project/infrastructure/kubernetes/show.rb3
-rw-r--r--qa/qa/page/project/issue/show.rb4
-rw-r--r--qa/qa/page/project/job/show.rb2
-rw-r--r--qa/qa/page/project/monitor/alerts/index.rb21
-rw-r--r--qa/qa/page/project/monitor/metrics/show.rb134
-rw-r--r--qa/qa/page/project/pipeline/new.rb28
-rw-r--r--qa/qa/page/project/pipeline/show.rb10
-rw-r--r--qa/qa/page/project/pipeline_editor/new.rb2
-rw-r--r--qa/qa/page/project/pipeline_editor/show.rb20
-rw-r--r--qa/qa/page/project/settings/alerts.rb56
-rw-r--r--qa/qa/page/project/settings/merge_request.rb2
-rw-r--r--qa/qa/page/project/settings/monitor.rb10
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb6
-rw-r--r--qa/qa/page/project/settings/repository.rb2
-rw-r--r--qa/qa/page/project/settings/services/jenkins.rb2
-rw-r--r--qa/qa/page/project/settings/services/jira.rb2
-rw-r--r--qa/qa/page/project/settings/services/pipeline_status_emails.rb3
-rw-r--r--qa/qa/page/project/settings/services/prometheus.rb36
-rw-r--r--qa/qa/page/project/show.rb5
-rw-r--r--qa/qa/page/project/sub_menus/monitor.rb8
-rw-r--r--qa/qa/page/project/web_ide/edit.rb8
-rw-r--r--qa/qa/resource/api_fabricator.rb4
-rw-r--r--qa/qa/resource/base.rb6
-rw-r--r--qa/qa/resource/bulk_import_group.rb8
-rw-r--r--qa/qa/resource/group.rb10
-rw-r--r--qa/qa/resource/group_base.rb1
-rw-r--r--qa/qa/resource/issuable.rb3
-rw-r--r--qa/qa/resource/merge_request.rb4
-rw-r--r--qa/qa/resource/protected_branch.rb4
-rw-r--r--qa/qa/resource/reusable.rb4
-rw-r--r--qa/qa/resource/runner.rb166
-rw-r--r--qa/qa/resource/sandbox.rb2
-rw-r--r--qa/qa/resource/ssh_key.rb4
-rw-r--r--qa/qa/resource/user.rb2
-rw-r--r--qa/qa/runtime/api/client.rb8
-rw-r--r--qa/qa/runtime/api/repository_storage_moves.rb2
-rw-r--r--qa/qa/runtime/browser.rb5
-rw-r--r--qa/qa/runtime/env.rb11
-rw-r--r--qa/qa/runtime/ip_address.rb13
-rw-r--r--qa/qa/runtime/logger.rb26
-rw-r--r--qa/qa/runtime/script_extensions/interceptor.js50
-rw-r--r--qa/qa/scenario/test/integration/import.rb13
-rw-r--r--qa/qa/service/docker_run/base.rb6
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb6
-rw-r--r--qa/qa/service/praefect_manager.rb171
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb12
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb10
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb10
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb8
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb6
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb76
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb33
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb196
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_members_spec.rb32
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb69
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb50
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb10
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb11
-rw-r--r--qa/qa/specs/features/api/4_verify/file_variable_spec.rb5
-rw-r--r--qa/qa/specs/features/api/4_verify/remove_runner_spec.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb77
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb45
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb (renamed from qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb)22
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb40
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb15
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/web_terminal_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb90
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb93
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/mr_event_rule_pipeline_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb25
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb77
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/.gitkeep0
-rw-r--r--qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb36
-rw-r--r--qa/qa/specs/features/sanity/interception_spec.rb39
-rw-r--r--qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb14
-rw-r--r--qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb106
-rw-r--r--qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb77
-rw-r--r--qa/qa/specs/helpers/feature_flag.rb4
-rw-r--r--qa/qa/specs/spec_helper.rb8
-rw-r--r--qa/qa/support/data/github.rb15
-rw-r--r--qa/qa/support/data/license.rb27
-rw-r--r--qa/qa/support/formatters/allure_metadata_formatter.rb11
-rw-r--r--qa/qa/support/formatters/test_metrics_formatter.rb67
-rw-r--r--qa/qa/support/helpers/mask_token.rb6
-rw-r--r--qa/qa/support/influxdb_tools.rb2
-rw-r--r--qa/qa/support/knapsack_report.rb6
-rw-r--r--qa/qa/support/loglinking.rb108
-rw-r--r--qa/qa/support/page/logging.rb5
-rw-r--r--qa/qa/support/page_error_checker.rb12
-rw-r--r--qa/qa/support/run.rb4
-rw-r--r--qa/qa/support/ssh.rb2
-rw-r--r--qa/qa/tools/long_running_spec_reporter.rb6
-rw-r--r--qa/qa/tools/reliable_report.rb6
-rw-r--r--qa/qa/tools/test_resources_handler.rb4
-rw-r--r--qa/qa/vendor/smocker/event_payload.rb12
-rw-r--r--qa/qa/vendor/smocker/smocker_api.rb1
143 files changed, 1736 insertions, 1326 deletions
diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb
index 981b60d1920..8143595a18b 100644
--- a/qa/qa/ce/strategy.rb
+++ b/qa/qa/ce/strategy.rb
@@ -8,12 +8,12 @@ module QA
def perform_before_hooks
if QA::Runtime::Env.admin_personal_access_token.present?
QA::Resource::PersonalAccessTokenCache.set_token_for_username(QA::Runtime::User.admin_username,
- QA::Runtime::Env.admin_personal_access_token)
+ QA::Runtime::Env.admin_personal_access_token)
end
if QA::Runtime::Env.personal_access_token.present? && QA::Runtime::Env.user_username.present?
QA::Resource::PersonalAccessTokenCache.set_token_for_username(QA::Runtime::Env.user_username,
- QA::Runtime::Env.personal_access_token)
+ QA::Runtime::Env.personal_access_token)
end
# The login page could take some time to load the first time it is visited.
@@ -22,6 +22,9 @@ module QA
QA::Support::Retrier.retry_on_exception do
QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login)
end
+ return unless QA::Runtime::Env.allow_local_requests?
+
+ Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
end
end
end
diff --git a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
index 8eac8419022..e6ec4528d0d 100644
--- a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
+++ b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb
@@ -25,9 +25,26 @@ spec:
- --token-file=/config/token
- --kas-address
- "<%= kas_wss_address %>"
+ <% if QA::Runtime::Env.qa_cookies.to_s.include?("gitlab_canary=true") %>
+ - --kas-header
+ - "Cookie: gitlab_canary=true"
+ <% end %>
volumeMounts:
- name: token-volume
mountPath: /config
+ env:
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: SERVICE_ACCOUNT_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.serviceAccountName
volumes:
- name: token-volume
secret:
diff --git a/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
index 611c232819f..a2d3e7493ef 100644
--- a/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
+++ b/qa/qa/fixtures/package_managers/maven/group/settings_with_pat.xml.erb
@@ -7,7 +7,7 @@
<httpHeaders>
<property>
<name>Private-Token</name>
- <value><%= personal_access_token %></value>
+ <value>${PERSONAL_ACCESS_TOKEN}</value>
</property>
</httpHeaders>
</configuration>
diff --git a/qa/qa/flow/alert_settings.rb b/qa/qa/flow/alert_settings.rb
new file mode 100644
index 00000000000..0e884f58773
--- /dev/null
+++ b/qa/qa/flow/alert_settings.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module AlertSettings
+ extend self
+
+ def setup_http_endpoint_and_send_alert(integration_name: nil, payload: nil)
+ integration_name ||= random_word
+ payload ||= { title: random_word, description: random_word }
+ Page::Project::Menu.perform(&:go_to_monitor_settings)
+ Page::Project::Settings::Monitor.perform do |setting|
+ setting.expand_alerts do |alert|
+ alert.add_new_integration
+ alert.select_http_endpoint
+ alert.enter_integration_name(integration_name)
+ alert.activate_integration
+ alert.save_and_create_alert
+ alert.fill_in_test_payload(payload.to_json)
+ alert.send_test_alert
+ end
+ end
+ end
+
+ private
+
+ def random_word
+ Faker::Lorem.word
+ end
+ end
+ end
+end
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
index ec205e0aa86..564a51ee483 100644
--- a/qa/qa/flow/login.rb
+++ b/qa/qa/flow/login.rb
@@ -14,10 +14,8 @@ module QA
result
end
- def while_signed_in_as_admin(address: :gitlab)
- while_signed_in(address: address, admin: true) do
- yield
- end
+ def while_signed_in_as_admin(address: :gitlab, &block)
+ while_signed_in(address: address, admin: true, &block)
end
def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false)
diff --git a/qa/qa/git/location.rb b/qa/qa/git/location.rb
index c3733572e70..9ac97a66e53 100644
--- a/qa/qa/git/location.rb
+++ b/qa/qa/git/location.rb
@@ -1,16 +1,13 @@
# frozen_string_literal: true
require 'uri'
-require 'forwardable'
module QA
module Git
class Location
- extend Forwardable
-
attr_reader :git_uri, :uri
- def_delegators :@uri, :user, :host, :path
+ delegate :user, :host, :path, to: :@uri
# See: config/initializers/1_settings.rb
# Settings#build_gitlab_shell_ssh_path_prefix
diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb
index 3164676f8e4..42dd1083bbe 100644
--- a/qa/qa/page/admin/menu.rb
+++ b/qa/qa/page/admin/menu.rb
@@ -92,16 +92,12 @@ module QA
end
end
- def within_sidebar
- within_element(:admin_sidebar_content) do
- yield
- end
+ def within_sidebar(&block)
+ within_element(:admin_sidebar_content, &block)
end
- def within_submenu(element)
- within_element(element) do
- yield
- end
+ def within_submenu(element, &block)
+ within_element(element, &block)
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index f59b06b4e75..ab83da7dacf 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -13,7 +13,6 @@ module QA
include Support::WaitForRequests
extend Validatable
- extend SingleForwardable
ElementNotFound = Class.new(RuntimeError)
@@ -31,8 +30,6 @@ module QA
end
end
- def_delegators :evaluator, :view, :views
-
def initialize
@retry_later_backoff = QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME
end
@@ -276,7 +273,7 @@ module QA
visible = kwargs.delete(:visible)
visible = visible.nil? && true
- try_find_element = lambda do |wait|
+ try_find_element = ->(wait) do
if disabled.nil?
has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass, visible: visible)
else
@@ -422,26 +419,30 @@ module QA
URI(page.current_url).host
end
- def self.path
- raise NotImplementedError
- end
+ class << self
+ def path
+ raise NotImplementedError
+ end
- def self.evaluator
- @evaluator ||= Page::Base::DSL.new
- end
+ def evaluator
+ @evaluator ||= Page::Base::DSL.new
+ end
- def self.errors
- return ["Page class does not have views / elements defined!"] if views.empty?
+ def errors
+ return ["Page class does not have views / elements defined!"] if views.empty?
- views.flat_map(&:errors)
- end
+ views.flat_map(&:errors)
+ end
- def self.elements
- views.flat_map(&:elements)
- end
+ def elements
+ views.flat_map(&:elements)
+ end
+
+ def required_elements
+ elements.select(&:required?)
+ end
- def self.required_elements
- elements.select(&:required?)
+ delegate :view, :views, to: :evaluator
end
def send_keys_to_element(name, keys)
diff --git a/qa/qa/page/component/blob_content.rb b/qa/qa/page/component/blob_content.rb
index b6001cf39b5..a57ef38f768 100644
--- a/qa/qa/page/component/blob_content.rb
+++ b/qa/qa/page/component/blob_content.rb
@@ -72,11 +72,11 @@ module QA
private
- def within_file_by_number(element, file_number)
+ def within_file_by_number(element, file_number, &block)
if file_number
- within_element_by_index(element, file_number - 1) { yield }
+ within_element_by_index(element, file_number - 1, &block)
else
- within_element(element) { yield }
+ within_element(element, &block)
end
end
end
diff --git a/qa/qa/page/component/custom_metric.rb b/qa/qa/page/component/custom_metric.rb
deleted file mode 100644
index 094979f5e18..00000000000
--- a/qa/qa/page/component/custom_metric.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Component
- module CustomMetric
- extend QA::Page::PageConcern
-
- def self.included(base)
- super
-
- base.view 'app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue' do
- element :custom_metric_prometheus_title_field
- element :custom_metric_prometheus_query_field
- element :custom_metric_prometheus_y_label_field
- element :custom_metric_prometheus_unit_label_field
- element :custom_metric_prometheus_legend_label_field
- end
- end
-
- def add_custom_metric
- fill_element :custom_metric_prometheus_title_field, 'HTTP Requests Total'
- fill_element :custom_metric_prometheus_query_field, 'rate(http_requests_total[5m])'
- fill_element :custom_metric_prometheus_y_label_field, 'Requests/second'
- fill_element :custom_metric_prometheus_unit_label_field, 'req/sec'
- fill_element :custom_metric_prometheus_legend_label_field, 'HTTP requests'
-
- save_changes
- end
-
- def save_changes
- click_button(class: 'btn-success')
- end
-
- def delete_custom_metric
- click_button(class: 'btn-danger')
- within('.modal-content') { click_button(class: 'btn-danger') }
- end
-
- def edit_custom_metric
- fill_element :custom_metric_prometheus_title_field, ''
- fill_element :custom_metric_prometheus_title_field, 'Throughput'
-
- save_changes
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/component/dropdown.rb b/qa/qa/page/component/dropdown.rb
new file mode 100644
index 00000000000..e6204fb5332
--- /dev/null
+++ b/qa/qa/page/component/dropdown.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+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
+ end
+
+ def has_item?(item_text)
+ return super if use_select2?
+
+ has_css?('li.gl-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
+ 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"]')
+ find('button[data-testid="listbox-reset-button"]').click
+ elsif dropdown_open?
+ expand_select_list
+ end
+ end
+
+ def search_item(item_text)
+ return super if use_select2?
+
+ find('div.gl-search-box-by-type input[type="Search"]').set(item_text)
+ wait_for_search_to_complete
+ 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)
+ raise QA::Page::Base::ElementNotFound, %(Couldn't find option named "#{item_text}")
+ end
+
+ select_item(item_text)
+ end
+
+ def search_and_select_exact(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)
+ 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
+ end
+
+ def expand_select_list
+ return super if use_select2?
+
+ find('svg.dropdown-chevron').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)
+ has_no_css?('div[data-testid="listbox-search-loader"]')
+ end
+
+ def dropdown_open?
+ return super if use_select2?
+
+ has_css?('ul.gl-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
+end
diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb
index 27dce152367..295b5134bd5 100644
--- a/qa/qa/page/component/invite_members_modal.rb
+++ b/qa/qa/page/component/invite_members_modal.rb
@@ -76,10 +76,7 @@ module QA
def set_access_level(access_level)
# Guest option is selected by default, skipping these steps if desired option is 'Guest'
- unless access_level == 'Guest'
- click_element :access_level_dropdown
- click_button access_level
- end
+ select_element(:access_level_dropdown, access_level) unless access_level == 'Guest'
end
def send_invite
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index fb2e7478684..0a31dee2b4f 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -22,19 +22,19 @@ module QA
element :reviewers_edit_button
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/labels_select_root.vue' do
element :labels_block
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_vue/dropdown_contents_labels_view.vue' do
element :dropdown_input_field
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_contents.vue' do
element :labels_dropdown_content
end
- base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/labels_select_widget/dropdown_value.vue' do
element :selected_label_content
end
diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb
index 47ed1a9616b..4e1c7f3e2bb 100644
--- a/qa/qa/page/component/snippet.rb
+++ b/qa/qa/page/component/snippet.rb
@@ -170,7 +170,7 @@ module QA
# wait for the page to reload after deletion
wait_until(reload: false) do
has_no_element?(:delete_snippet_button) &&
- has_no_element?(:snippet_action_button, action: 'Delete')
+ has_no_element?(:snippet_action_button, action: 'Delete')
end
end
diff --git a/qa/qa/page/file/edit.rb b/qa/qa/page/file/edit.rb
index b9b676ee3c4..e66019279ce 100644
--- a/qa/qa/page/file/edit.rb
+++ b/qa/qa/page/file/edit.rb
@@ -8,8 +8,8 @@ module QA
include Shared::CommitButton
include Shared::Editor
- view 'app/assets/javascripts/editor/components/source_editor_toolbar_button.vue' do
- element :editor_toolbar_button
+ view 'app/assets/javascripts/editor/extensions/source_editor_markdown_livepreview_ext.js' do
+ element :editor_toolbar_button, "qaSelector: 'editor_toolbar_button'" # rubocop:disable QA/ElementWithPattern
end
def has_markdown_preview?(component, content)
diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb
index 86585eee121..bb5a3485531 100644
--- a/qa/qa/page/group/settings/general.rb
+++ b/qa/qa/page/group/settings/general.rb
@@ -40,6 +40,14 @@ module QA
element :project_creation_level_dropdown
end
+ view 'app/views/groups/settings/_transfer.html.haml' do
+ element :transfer_group_content
+ end
+
+ view 'app/assets/javascripts/groups/components/transfer_group_form.vue' do
+ element :transfer_group_button
+ end
+
def set_group_name(name)
find_element(:group_name_field).send_keys([:command, 'a'], :backspace)
find_element(:group_name_field).set name
@@ -102,6 +110,38 @@ module QA
click_element(:save_permissions_changes_button)
end
+
+ def transfer_group(source_group, target_group)
+ QA::Runtime::Logger.info "Transferring group: #{source_group.path} to target group: #{target_group.path}"
+
+ expand_content(:advanced_settings_content)
+
+ scroll_to_transfer_group_content
+
+ select_namespace(target_group.path)
+
+ wait_for_enabled_transfer_group_button
+ click_element(:transfer_group_button)
+
+ fill_confirmation_text(source_group.path)
+ confirm_transfer
+ end
+
+ private
+
+ def scroll_to_transfer_group_content
+ retry_until(sleep_interval: 1, message: 'Waiting for transfer group content to display') do
+ has_element?(:transfer_group_content, wait: 3)
+ end
+
+ scroll_to_element :transfer_group_content
+ end
+
+ def wait_for_enabled_transfer_group_button
+ retry_until(sleep_interval: 1, message: 'Waiting for transfer group button to be enabled') do
+ has_element?(:transfer_group_button, disabled: false, wait: 3)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/group/settings/group_deploy_tokens.rb b/qa/qa/page/group/settings/group_deploy_tokens.rb
index 7d908f473de..c1c3303113b 100644
--- a/qa/qa/page/group/settings/group_deploy_tokens.rb
+++ b/qa/qa/page/group/settings/group_deploy_tokens.rb
@@ -54,12 +54,10 @@ module QA
private
- def within_new_project_deploy_token
+ def within_new_project_deploy_token(&block)
has_element?(:created_deploy_token_container, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
- within_element(:created_deploy_token_container) do
- yield
- end
+ within_element(:created_deploy_token_container, &block)
end
end
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index a30d489e6ff..46ab1e35510 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -39,7 +39,7 @@ module QA
end
def group_id
- find_element(:group_id_content).text.delete('Group ID: ')
+ find_element(:group_id_content).text.delete('Group ID: ').sub(/\n.*/, '')
end
def leave_group
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index d7ca8223862..8af78bb86c6 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -12,6 +12,10 @@ module QA
element :change_password_button
end
+ view 'app/views/devise/sessions/new.html.haml' do
+ element :register_link
+ end
+
view 'app/views/devise/sessions/_new_base.html.haml' do
element :login_field
element :password_field
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index ecd71e7c2f4..1e050d79e23 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -9,6 +9,7 @@ module QA
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :sign_out_link
element :edit_profile_link
+ element :user_profile_link
end
view 'app/views/layouts/header/_default.html.haml' do
@@ -39,6 +40,7 @@ module QA
element :projects_dropdown
element :groups_dropdown
element :snippets_link
+ element :menu_item_link
end
view 'app/views/layouts/_search.html.haml' do
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index 909b37943ff..dc2f908a906 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -13,7 +13,7 @@ module QA
element :source_branch_dropdown
end
- view 'app/views/projects/merge_requests/show.html.haml' do
+ view 'app/views/projects/merge_requests/_page.html.haml' do
element :diffs_tab
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index e1add9ad434..aacff7c4172 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -126,7 +126,7 @@ module QA
element :title_content, required: true
end
- view 'app/views/projects/merge_requests/show.html.haml' do
+ view 'app/views/projects/merge_requests/_page.html.haml' do
element :notes_tab, required: true
element :commits_tab, required: true
element :diffs_tab, required: true
@@ -366,7 +366,7 @@ module QA
# Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042
# To remove page refresh logic if possible
wait_until_ready_to_merge
- wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') }
+ wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') } # rubocop:disable Rails/NegateInclude
click_element(:merge_button)
end
@@ -390,6 +390,7 @@ module QA
def click_open_in_web_ide
click_element(:mr_code_dropdown)
click_element(:open_in_web_ide_button)
+ page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
wait_for_requests
end
@@ -433,7 +434,11 @@ module QA
end
def revert_change!
- click_element(:revert_button, Page::Component::CommitModal)
+ # retry when the modal doesn't appear for large MRs as the onClick listener is initialized after the click
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/366336
+ retry_on_exception do
+ click_element(:revert_button, Page::Component::CommitModal)
+ end
click_element(:submit_commit_button)
end
diff --git a/qa/qa/page/profile/two_factor_auth.rb b/qa/qa/page/profile/two_factor_auth.rb
index 16aa60262d8..2add02b5c48 100644
--- a/qa/qa/page/profile/two_factor_auth.rb
+++ b/qa/qa/page/profile/two_factor_auth.rb
@@ -25,7 +25,8 @@ module QA
def click_configure_it_later_button
# TO DO: Investigate why button does not appear sometimes:
# https://gitlab.com/gitlab-org/gitlab/-/issues/382698
- return unless has_element?(:configure_it_later_button)
+ page.refresh
+ return unless has_element?(:configure_it_later_button, wait: 60)
click_element :configure_it_later_button
wait_until(max_duration: 10, message: "Waiting for create a group page") do
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index 75468c74814..c48b1a67d90 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -81,7 +81,19 @@ module QA
reload: false,
skip_finished_loading_check_on_refresh: true
) do
- has_element?(:import_status_indicator, text: "Complete")
+ status_selector = 'import_status_indicator'
+ is_partial_import = has_css?(status_selector, text: "Partial import")
+
+ # Temporarily adding this for investigation purposes. This makes sure that the details section is
+ # expanded when the screenshot is taken when the test fails. This can be removed or repurposed later
+ # after investigation. Related: https://gitlab.com/gitlab-org/gitlab/-/issues/385252#note_1211218434
+ if is_partial_import
+ within_element_by_index(:import_status_indicator, 0) do
+ find('button').click
+ end
+ end
+
+ has_element?(status_selector, text: "Complete")
end
end
end
diff --git a/qa/qa/page/project/infrastructure/kubernetes/show.rb b/qa/qa/page/project/infrastructure/kubernetes/show.rb
index 6de5024e525..8725f64fe32 100644
--- a/qa/qa/page/project/infrastructure/kubernetes/show.rb
+++ b/qa/qa/page/project/infrastructure/kubernetes/show.rb
@@ -9,6 +9,9 @@ module QA
view 'app/assets/javascripts/clusters/forms/components/integration_form.vue' do
element :integration_status_toggle
element :base_domain_field
+ end
+
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index b1417d9b9db..2f8ffc634ac 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -67,10 +67,6 @@ module QA
click_element :close_issue_button
end
- def has_metrics_unfurled?
- has_element?(:prometheus_graph_widgets, wait: 30)
- end
-
def has_reopen_issue_button?
has_element?(:reopen_issue_button)
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 5506c5ed4d9..24fd34b4d22 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -15,7 +15,7 @@ module QA
element :pipeline_path, required: true
end
- view 'app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue' do
element :retry_button
end
diff --git a/qa/qa/page/project/monitor/alerts/index.rb b/qa/qa/page/project/monitor/alerts/index.rb
new file mode 100644
index 00000000000..50b69d59db7
--- /dev/null
+++ b/qa/qa/page/project/monitor/alerts/index.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Monitor
+ module Alerts
+ class Index < Page::Base
+ view 'app/assets/javascripts/alert_management/components/alert_management_table.vue' do
+ element :alert_table_container, required: true
+ end
+
+ def has_alert_with_title?(title)
+ has_link?(title)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/monitor/metrics/show.rb b/qa/qa/page/project/monitor/metrics/show.rb
deleted file mode 100644
index 59602d0fcf7..00000000000
--- a/qa/qa/page/project/monitor/metrics/show.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Monitor
- module Metrics
- class Show < Page::Base
- EXPECTED_TITLE = 'Memory Usage (Total)'
- LOADING_MESSAGE = 'Waiting for performance data'
-
- view 'app/assets/javascripts/monitoring/components/dashboard.vue' do
- element :prometheus_graphs_content
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
- element :dashboards_filter_dropdown
- element :environments_dropdown
- element :range_picker_dropdown
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue' do
- element :actions_menu_dropdown
- element :edit_dashboard_button_enabled
- end
-
- view 'app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue' do
- element :duplicate_dashboard_filename_field
- end
-
- view 'app/assets/javascripts/monitoring/components/dashboard_panel.vue' do
- element :prometheus_graph_widgets
- element :prometheus_widgets_dropdown
- element :generate_chart_link_menu_item
- end
-
- view 'app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue' do
- element :quick_range_item
- end
-
- view 'app/assets/javascripts/monitoring/components/variables_section.vue' do
- element :variables_content
- element :variable_item
- end
-
- def wait_for_metrics
- wait_for_data
- return if has_metrics?
-
- wait_until(max_duration: 180) do
- wait_for_data
- has_metrics?
- end
- end
-
- def has_metrics?
- within_element :prometheus_graphs_content do
- has_text?(EXPECTED_TITLE)
- end
- end
-
- def has_edit_dashboard_enabled?
- click_element :actions_menu_dropdown
-
- within_element :actions_menu_dropdown do
- has_element? :edit_dashboard_button_enabled
- end
- end
-
- def duplicate_dashboard(save_as = 'test_duplication.yml', commit_option = 'Commit to default branch')
- click_element :actions_menu_dropdown
- click_on 'Duplicate current dashboard'
- fill_element :duplicate_dashboard_filename_field, "#{SecureRandom.hex(8)}-#{save_as}"
- choose commit_option
- within('.modal-content') { click_button(class: 'btn-success') }
- end
-
- def select_dashboard(dashboard_name)
- click_element :dashboards_filter_dropdown
-
- within_element :dashboards_filter_dropdown do
- click_on dashboard_name
- end
- end
-
- def filter_environment(environment = 'production')
- click_element :environments_dropdown
-
- within_element :environments_dropdown do
- click_link_with_text environment
- end
- end
-
- def show_last(range = '8 hours')
- all_elements(:range_picker_dropdown, minimum: 1).first.click
- click_element :quick_range_item, text: range
- end
-
- def copy_link_to_first_chart
- all_elements(:prometheus_widgets_dropdown, minimum: 1).first.click
- find_element(:generate_chart_link_menu_item)['data-clipboard-text']
- end
-
- def has_custom_metric?(metric)
- within_element :prometheus_graphs_content do
- has_text?(metric)
- end
- end
-
- def has_templating_variable?(variable)
- within_element :variables_content do
- has_element?(:variable_item, text: variable)
- end
- end
-
- def has_template_metric?(metric)
- within_element :prometheus_graphs_content do
- has_text?(metric)
- end
- end
-
- private
-
- def wait_for_data
- wait_until(reload: false) { !has_text?(LOADING_MESSAGE) } if has_text?(LOADING_MESSAGE)
- end
- end
- end
- end
- end
- end
-end
-
-QA::Page::Project::Monitor::Metrics::Show.prepend_mod_with('Page::Project::Monitor::Metrics::Show', namespace: QA)
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index 742fcad5c07..1d85d072e34 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -5,23 +5,49 @@ module QA
module Project
module Pipeline
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue' do
+ view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
element :run_pipeline_button, required: true
element :ci_variable_row_container
element :ci_variable_key_field
element :ci_variable_value_field
+ element :ci_variable_value_dropdown
+ element :ci_variable_value_dropdown_item
end
def click_run_pipeline_button
click_element(:run_pipeline_button, Page::Project::Pipeline::Show)
end
+ def click_variable_dropdown
+ return unless has_variable_dropdown?
+
+ click_element(:ci_variable_value_dropdown)
+ end
+
def configure_variable(key: nil, value: 'foo', row_index: 0)
within_element_by_index(:ci_variable_row_container, row_index) do
fill_element(:ci_variable_key_field, key) unless key.nil?
fill_element(:ci_variable_value_field, value)
end
end
+
+ def has_variable_dropdown?
+ has_element?(:ci_variable_value_dropdown)
+ end
+
+ def variable_dropdown
+ return unless has_variable_dropdown?
+
+ find_element(:ci_variable_value_dropdown)
+ end
+
+ def variable_dropdown_item_with_index(index)
+ return unless has_variable_dropdown?
+
+ within_element_by_index(:ci_variable_value_dropdown_item, index) do
+ find('p')
+ end
+ end
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 33ba27a788a..e4511ababfd 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -11,6 +11,10 @@ module QA
element :pipeline_header, required: true
end
+ view 'app/views/projects/pipelines/_info.html.haml' do
+ element :merge_request_badge_tag
+ end
+
view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do
element :pipeline_graph, /class.*pipeline-graph.*/ # rubocop:disable QA/ElementWithPattern
end
@@ -27,7 +31,7 @@ module QA
element :downstream_title_content
end
- view 'app/assets/javascripts/reports/components/report_section.vue' do
+ view 'app/assets/javascripts/ci/reports/components/report_section.vue' do
element :expand_report_button
end
@@ -46,6 +50,10 @@ module QA
end
end
+ def has_merge_request_badge_tag?
+ has_element?(:merge_request_badge_tag)
+ end
+
def has_build?(name, status: :success, wait: nil)
if status
within_element(:job_item_container, text: name) do
diff --git a/qa/qa/page/project/pipeline_editor/new.rb b/qa/qa/page/project/pipeline_editor/new.rb
index 5d79dd86f2a..da3e772b11f 100644
--- a/qa/qa/page/project/pipeline_editor/new.rb
+++ b/qa/qa/page/project/pipeline_editor/new.rb
@@ -5,7 +5,7 @@ module QA
module Project
module PipelineEditor
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_editor/components/ui/pipeline_editor_empty_state.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/ui/pipeline_editor_empty_state.vue' do
element :create_new_ci_button, required: true
end
diff --git a/qa/qa/page/project/pipeline_editor/show.rb b/qa/qa/page/project/pipeline_editor/show.rb
index 8fa20aa57cf..0a7a4460d18 100644
--- a/qa/qa/page/project/pipeline_editor/show.rb
+++ b/qa/qa/page/project/pipeline_editor/show.rb
@@ -5,21 +5,21 @@ module QA
module Project
module PipelineEditor
class Show < QA::Page::Base
- view 'app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/pipeline_editor_app.vue' do
element :pipeline_editor_app, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue' do
element :branch_selector_button, required: true
element :branch_menu_item_button
element :branch_menu_container
end
- view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue' do
element :source_branch_field, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/editor/ci_editor_header.vue' do
element :drawer_toggle, required: true
element :template_repo_link, required: true
end
@@ -28,16 +28,16 @@ module QA
element :source_editor_container, required: true
end
- view 'app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/header/pipeline_status.vue' do
element :pipeline_id_content
end
- view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/commit/commit_form.vue' do
element :commit_changes_button
element :new_mr_checkbox
end
- view 'app/assets/javascripts/pipeline_editor/components/header/validation_segment.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/header/validation_segment.vue' do
element :validation_message_content
end
@@ -46,15 +46,15 @@ module QA
element :job_container
end
- view 'app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/pipeline_editor_tabs.vue' do
element :file_editor_container
end
- view 'app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/popovers/file_tree_popover.vue' do
element :file_tree_popover
end
- view 'app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue' do
+ view 'app/assets/javascripts/ci/pipeline_editor/components/validate/ci_validate.vue' do
element :simulate_pipeline_button
end
diff --git a/qa/qa/page/project/settings/alerts.rb b/qa/qa/page/project/settings/alerts.rb
index be9b61ded80..a74a227d697 100644
--- a/qa/qa/page/project/settings/alerts.rb
+++ b/qa/qa/page/project/settings/alerts.rb
@@ -6,14 +6,27 @@ module QA
module Settings
class Alerts < Page::Base
view 'app/assets/javascripts/alerts_settings/components/alerts_form.vue' do
- element :create_issue_checkbox
+ element :create_incident_checkbox
element :incident_templates_dropdown
element :save_changes_button
element :incident_templates_item
end
- def enable_issues_for_incidents
- check_element(:create_issue_checkbox)
+ view 'app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue' do
+ element :add_integration_button
+ end
+
+ view 'app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue' do
+ element :integration_type_dropdown
+ element :integration_name_field
+ element :active_toggle_container
+ element :save_and_create_alert_button
+ element :test_payload_field
+ element :send_test_alert_button
+ end
+
+ def enable_incident_for_alert
+ check_element(:create_incident_checkbox)
end
def select_issue_template(template)
@@ -32,6 +45,43 @@ module QA
has_text?(template)
end
end
+
+ def add_new_integration
+ wait_for_requests
+ click_element(:add_integration_button)
+ end
+
+ def select_http_endpoint
+ click_element(:integration_type_dropdown)
+ find("option[value='HTTP']").click
+
+ # Click outside of the list to close it
+ click_element(:integration_name_field)
+ end
+
+ def enter_integration_name(name)
+ fill_element(:integration_name_field, name)
+ end
+
+ def activate_integration
+ within_element(:active_toggle_container) do
+ find('.gl-toggle').click
+ end
+
+ wait_for_requests
+ end
+
+ def save_and_create_alert
+ click_element(:save_and_create_alert_button)
+ end
+
+ def fill_in_test_payload(payload)
+ fill_element(:test_payload_field, payload)
+ end
+
+ def send_test_alert
+ click_element(:send_test_alert_button)
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index d862979aeec..ae6a04028c9 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -15,7 +15,7 @@ module QA
element :merge_ff_radio
end
- view 'app/views/projects/_merge_request_merge_checks_settings.html.haml' do
+ view 'app/views/projects/_merge_request_pipelines_and_threads_options.html.haml' do
element :allow_merge_if_all_discussions_are_resolved_checkbox
end
diff --git a/qa/qa/page/project/settings/monitor.rb b/qa/qa/page/project/settings/monitor.rb
index 87fb0698897..8170ae31a13 100644
--- a/qa/qa/page/project/settings/monitor.rb
+++ b/qa/qa/page/project/settings/monitor.rb
@@ -11,8 +11,18 @@ module QA
element :incidents_settings_content
end
+ view 'app/views/projects/settings/operations/_alert_management.html.haml' do
+ element :alerts_settings_content
+ end
+
def expand_incidents(&block)
expand_content(:incidents_settings_content) do
+ # Fill in with incidents settings
+ end
+ end
+
+ def expand_alerts(&block)
+ expand_content(:alerts_settings_content) do
Settings::Alerts.perform(&block)
end
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 4fbf656210f..659fe198d49 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -5,19 +5,19 @@ module QA
module Project
module Settings
class ProtectedBranches < Page::Base
- view 'app/views/projects/protected_branches/shared/_dropdown.html.haml' do
+ view 'app/views/protected_branches/shared/_dropdown.html.haml' do
element :protected_branch_dropdown
element :protected_branch_dropdown_content
end
- view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
+ view 'app/views/protected_branches/_create_protected_branch.html.haml' do
element :allowed_to_push_dropdown
element :allowed_to_push_dropdown_content
element :allowed_to_merge_dropdown
element :allowed_to_merge_dropdown_content
end
- view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
+ view 'app/views/protected_branches/shared/_create_protected_branch.html.haml' do
element :protect_button
end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index bf1c3130485..6931d26b259 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -7,7 +7,7 @@ module QA
class Repository < Page::Base
include QA::Page::Settings::Common
- view 'app/views/projects/protected_branches/shared/_index.html.haml' do
+ view 'app/views/protected_branches/shared/_index.html.haml' do
element :protected_branches_settings_content
end
diff --git a/qa/qa/page/project/settings/services/jenkins.rb b/qa/qa/page/project/settings/services/jenkins.rb
index 39403995ce8..a9b5c84f9ee 100644
--- a/qa/qa/page/project/settings/services/jenkins.rb
+++ b/qa/qa/page/project/settings/services/jenkins.rb
@@ -13,7 +13,7 @@ module QA
element :service_password_field, ':data-qa-selector="`${fieldId}_field`"' # rubocop:disable QA/ElementWithPattern
end
- view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/jira.rb b/qa/qa/page/project/settings/services/jira.rb
index 41034bbd897..7a62b111f98 100644
--- a/qa/qa/page/project/settings/services/jira.rb
+++ b/qa/qa/page/project/settings/services/jira.rb
@@ -19,7 +19,7 @@ module QA
element :service_jira_issue_transition_id_field
end
- view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/pipeline_status_emails.rb b/qa/qa/page/project/settings/services/pipeline_status_emails.rb
index 2f78577e3d5..3edd1d61d76 100644
--- a/qa/qa/page/project/settings/services/pipeline_status_emails.rb
+++ b/qa/qa/page/project/settings/services/pipeline_status_emails.rb
@@ -9,6 +9,9 @@ module QA
view 'app/assets/javascripts/integrations/edit/components/integration_form.vue' do
element :recipients_div, %q(:data-qa-selector="`${field.name}_div`") # rubocop:disable QA/ElementWithPattern
element :notify_only_broken_pipelines_div, %q(:data-qa-selector="`${field.name}_div`") # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/assets/javascripts/integrations/edit/components/integration_form_actions.vue' do
element :save_changes_button
end
diff --git a/qa/qa/page/project/settings/services/prometheus.rb b/qa/qa/page/project/settings/services/prometheus.rb
deleted file mode 100644
index 2e3c385a27d..00000000000
--- a/qa/qa/page/project/settings/services/prometheus.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Settings
- module Services
- class Prometheus < Page::Base
- include Page::Component::CustomMetric
-
- view 'app/views/shared/integrations/prometheus/_custom_metrics.html.haml' do
- element :custom_metrics_container
- element :new_metric_button
- end
-
- def click_on_custom_metric(custom_metric)
- within_element :custom_metrics_container do
- click_on custom_metric
- end
- end
-
- def click_on_new_metric
- click_element :new_metric_button
- end
-
- def has_custom_metric?(custom_metric)
- within_element :custom_metrics_container do
- has_text? custom_metric
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index a82fa7f5cf3..168bfd6aa0a 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -36,7 +36,6 @@ module QA
end
view 'app/views/projects/_home_panel.html.haml' do
- element :forked_from_link
element :project_name_content
element :project_id_content
element :project_badges_content
@@ -48,6 +47,10 @@ module QA
element :tree_holder, '.tree-holder' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/projects/_fork_info.html.haml' do
+ element :forked_from_link
+ end
+
view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('ProjectOverview|Fork')" # rubocop:disable QA/ElementWithPattern
element :fork_link, "link_to new_project_fork_path(@project)" # rubocop:disable QA/ElementWithPattern
diff --git a/qa/qa/page/project/sub_menus/monitor.rb b/qa/qa/page/project/sub_menus/monitor.rb
index e3593e0a257..27fb58fb146 100644
--- a/qa/qa/page/project/sub_menus/monitor.rb
+++ b/qa/qa/page/project/sub_menus/monitor.rb
@@ -15,18 +15,18 @@ module QA
end
end
- def go_to_monitor_metrics
+ def go_to_monitor_incidents
hover_monitor do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Metrics')
+ click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
end
end
end
- def go_to_monitor_incidents
+ def go_to_monitor_alerts
hover_monitor do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Incidents')
+ click_element(:sidebar_menu_item_link, menu_item: 'Alerts')
end
end
end
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
index 293fcd1e676..975d3c8ea14 100644
--- a/qa/qa/page/project/web_ide/edit.rb
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -109,6 +109,14 @@ module QA
element :file_to_commit_content
end
+ # Used for stablility, due to feature_caching of vscode_web_ide
+ def wait_until_ide_loads
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ has_element?(:commit_mode_tab)
+ end
+ end
+
def has_file?(file_name)
within_element(:file_list_container) do
has_element?(:file_name_content, file_name: file_name)
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb
index d1cfdfbc16c..d82109c1d54 100644
--- a/qa/qa/resource/api_fabricator.rb
+++ b/qa/qa/resource/api_fabricator.rb
@@ -170,9 +170,7 @@ module QA
end
def api_client
- @api_client ||= begin
- Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: api_user)
- end
+ @api_client ||= Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: api_user)
end
def process_api_response(parsed_response)
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index 4a1a60f4da1..00c002cae9c 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -23,7 +23,7 @@ module QA
end
def fabricate_via_api_unless_fips!
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
fabricate!
else
fabricate_via_api!
@@ -31,7 +31,7 @@ module QA
end
def fabricate!(*args, &prepare_block)
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
fabricate_via_browser_ui!(*args, &prepare_block)
else
fabricate_via_api!(*args, &prepare_block)
@@ -107,7 +107,7 @@ module QA
Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time)
- unless resource.retrieved_from_cache || QA::Support::FIPS.enabled?
+ unless resource.retrieved_from_cache || Runtime::Env.personal_access_tokens_disabled?
Tools::TestResourceDataProcessor.collect(
resource: resource,
info: resource.identifier,
diff --git a/qa/qa/resource/bulk_import_group.rb b/qa/qa/resource/bulk_import_group.rb
index 31db8ae4cc6..19ad5f1faf2 100644
--- a/qa/qa/resource/bulk_import_group.rb
+++ b/qa/qa/resource/bulk_import_group.rb
@@ -11,7 +11,7 @@ module QA
api_client.personal_access_token
end
- attribute :gitlab_address do
+ attribute :source_gitlab_address do
QA::Runtime::Scenario.gitlab_address
end
@@ -28,7 +28,7 @@ module QA
Page::Group::New.perform do |group|
group.switch_to_import_tab
- group.connect_gitlab_instance(gitlab_address, import_access_token)
+ group.connect_gitlab_instance(source_gitlab_address, import_access_token)
end
Page::Group::BulkImport.perform do |import_page|
@@ -50,7 +50,7 @@ module QA
def api_post_body
{
configuration: {
- url: gitlab_address,
+ url: source_gitlab_address,
access_token: import_access_token
},
entities: [
@@ -93,7 +93,7 @@ module QA
# override transformation only for /bulk_imports endpoint which doesn't have web_url in response and
# ignore others so import_id is not overwritten incorrectly
- api_resource[:web_url] = "#{gitlab_address}/#{full_path}"
+ api_resource[:web_url] = "#{QA::Runtime::Scenario.gitlab_address}/#{full_path}"
api_resource[:import_id] = api_resource[:id]
api_resource
end
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 9d1a6868562..f53bb531d9a 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -18,7 +18,7 @@ module QA
end
attribute :sandbox do
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
Resource::Sandbox.fabricate! do |sandbox|
sandbox.path = Runtime::Namespace.sandbox_name
end
@@ -40,9 +40,7 @@ module QA
sandbox.visit!
Page::Group::Show.perform do |group_show|
- if group_show.has_subgroup?(path)
- group_show.click_subgroup(path)
- else
+ unless group_show.has_subgroup?(path)
group_show.go_to_new_subgroup
Page::Group::New.perform do |group_new|
@@ -56,7 +54,11 @@ module QA
group_show.has_text?(path) &&
group_show.has_new_project_and_new_subgroup_buttons?
end
+ sandbox.visit!
end
+
+ group_show.click_subgroup(path)
+ @id = group_show.group_id
end
end
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index f6d1aacca0a..c5b1a4ecea0 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -24,6 +24,7 @@ module QA
def projects
parse_body(api_get_from("#{api_get_path}/projects")).map do |project|
Project.init do |resource|
+ resource.add_name_uuid = false
resource.api_client = api_client
resource.group = self
resource.id = project[:id]
diff --git a/qa/qa/resource/issuable.rb b/qa/qa/resource/issuable.rb
index 6ebdaac8298..5aee27b46d4 100644
--- a/qa/qa/resource/issuable.rb
+++ b/qa/qa/resource/issuable.rb
@@ -3,6 +3,8 @@
module QA
module Resource
class Issuable < Base
+ using Rainbow
+
# Commentes (notes) path
#
# @return [String]
@@ -14,6 +16,7 @@ module QA
#
# @return [Array]
def comments(auto_paginate: false, attempts: 0)
+ Runtime::Logger.debug("Fetching comments for #{self.class.name.black.bg(:white)} with path '#{api_get_path}'")
return parse_body(api_get_from(api_comments_path)) unless auto_paginate
auto_paginated_response(
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index fcfda106523..d1d99393ca2 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -199,6 +199,10 @@ module QA
:source_project_id,
:target_project_id,
:merge_status,
+ # we consider mr to still be the same even if users changed
+ :author,
+ :reviewers,
+ :assignees,
# these can differ depending on user fetching mr
:user,
:subscribed,
diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb
index 7b6a3d296c4..879c3a4282c 100644
--- a/qa/qa/resource/protected_branch.rb
+++ b/qa/qa/resource/protected_branch.rb
@@ -51,8 +51,8 @@ module QA
page.select_allowed_to_merge(allowed_to_merge)
page.select_allowed_to_push(allowed_to_push)
page.protect_branch
- else
- page.require_code_owner_approval(branch_name) if require_code_owner_approval
+ elsif require_code_owner_approval
+ page.require_code_owner_approval(branch_name)
end
end
end
diff --git a/qa/qa/resource/reusable.rb b/qa/qa/resource/reusable.rb
index 6a9d0392ba2..536f70b50b4 100644
--- a/qa/qa/resource/reusable.rb
+++ b/qa/qa/resource/reusable.rb
@@ -37,8 +37,8 @@ module QA
resource: self
}
- self.class.resources[reuse_as][:attributes] ||= all_attributes.each_with_object({}) do |attribute_name, attributes|
- attributes[attribute_name] = instance_variable_get("@#{attribute_name}")
+ self.class.resources[reuse_as][:attributes] ||= all_attributes.index_with do |attribute_name|
+ instance_variable_get("@#{attribute_name}")
end
self.class.resources[reuse_as][:tests] << Runtime::Example.location
end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index da4021f89b7..3c74d8de21a 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -3,10 +3,22 @@
module QA
module Resource
class Runner < Base
- attr_writer :name, :tags, :image, :executor, :executor_image
- attr_accessor :config, :token, :run_untagged
+ attributes :id,
+ :active,
+ :paused,
+ :runner_type,
+ :online,
+ :status,
+ :ip_address,
+ :token,
+ :tags,
+ :config,
+ :run_untagged,
+ :name, # This attribute == runner[:description]
+ :image,
+ :executor,
+ :executor_image
- attribute :id
attribute :project do
Project.fabricate_via_api! do |resource|
resource.name = 'project-with-ci-cd'
@@ -14,81 +26,47 @@ module QA
end
end
- def name
- @name || "qa-runner-#{SecureRandom.hex(4)}"
+ def initialize
+ @tags = nil
+ @config = nil
+ @run_untagged = nil
+ @name = "qa-runner-#{SecureRandom.hex(4)}"
+ @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine'
+ @executor = :shell
+ @executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
end
- def image
- @image || 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine'
- end
-
- def executor
- @executor || :shell
- end
-
- def executor_image
- @executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
+ # Initially we only support fabricate
+ # via API
+ def fabricate!
+ fabricate_via_api!
end
+ # Start container and register runner
+ # Fetch via API and populate attributes
+ #
def fabricate_via_api!
- @docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
- QA::Support::Retrier.retry_on_exception(sleep_interval: 5) do
- runner.pull
- end
-
- runner.token = @token ||= project.runners_token
- runner.address = Runtime::Scenario.gitlab_address
- runner.tags = @tags if @tags
- runner.image = image
- runner.config = config if config
- runner.executor = executor
- runner.executor_image = executor_image if executor == :docker
- runner.run_untagged = run_untagged if run_untagged
- runner.register!
- end
+ start_container_and_register
+ populate_runner_attributes
end
def remove_via_api!
- runners = list_of_runners(tag_list: @tags)
-
- # If we have no runners, print the logs from the runner docker container in case they show why it isn't running.
- if runners.blank?
- dump_logs
-
- return
- end
-
- this_runner = runners.find { |runner| runner[:description] == name }
-
- # As above, but now we should have a specific runner. If not, print the logs from the runner docker container
- # to see if we can find out why the runner isn't running.
- unless this_runner
- dump_logs
-
- raise "Project #{project.path_with_namespace} does not have a runner with a description matching #{name} #{"or tags #{@tags}" if @tags&.any?}. Runners available: #{runners}"
- end
-
- @id = this_runner[:id]
-
super
ensure
- Service::DockerRun::GitlabRunner.new(name).remove!
- end
-
- def list_of_runners(tag_list: nil)
- url = tag_list ? "#{api_post_path}?tag_list=#{tag_list.compact.join(',')}" : api_post_path
- auto_paginated_response(request_url(url))
+ @docker_container.remove!
end
def reload!
- super if method(:running?).super_method.call
+ populate_runner_attributes
end
def api_delete_path
"/runners/#{id}"
end
- def api_get_path; end
+ def api_get_path
+ "/runners"
+ end
def api_post_path
"/runners"
@@ -96,15 +74,75 @@ module QA
def api_post_body; end
+ def not_found_by_tags?
+ url = "#{api_get_path}?tag_list=#{tags.compact.join(',')}"
+ auto_paginated_response(request_url(url)).empty?
+ end
+
+ def runners_list
+ runners_list = nil
+ url = tags ? "#{api_get_path}?tag_list=#{tags.compact.join(',')}" : api_get_path
+ Runtime::Logger.info('Looking for list of runners via API...')
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ runners_list = auto_paginated_response(request_url(url))
+ runners_list.present?
+ end
+
+ runners_list
+ end
+
+ def wait_until_online
+ Runtime::Logger.info('Waiting for runner to come online...')
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ this_runner[:status] == 'online'
+ end
+ end
+
+ def restart
+ Runtime::Logger.info("Restarting runner container #{name}...")
+ @docker_container.restart
+ wait_until_online
+ end
+
private
- def dump_logs
- if @docker_container.running?
- @docker_container.logs
- else
- QA::Runtime::Logger.debug("No runner container found named #{name}")
+ def start_container_and_register
+ @docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
+ Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ runner.pull
+ end
+
+ runner.token = @token ||= project.runners_token
+ runner.address = Runtime::Scenario.gitlab_address
+ runner.tags = tags if tags
+ runner.image = image
+ runner.config = config if config
+ runner.executor = executor
+ runner.executor_image = executor_image if executor == :docker
+ runner.run_untagged = run_untagged if run_untagged
+ runner.register!
end
end
+
+ def this_runner
+ runner = nil
+ Support::Retrier.retry_until(max_duration: 60, sleep_interval: 1) do
+ runner = runners_list.find { |runner| runner[:description] == name }
+ !runner.nil?
+ end
+ runner
+ end
+
+ def populate_runner_attributes
+ runner = this_runner
+ @id = runner[:id]
+ @active = runner[:active]
+ @paused = runner[:paused]
+ @runner_type = runner[:typed]
+ @online = runner[:online]
+ @status = runner[:status]
+ @ip_address = runner[:ip_address]
+ end
end
end
end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 18526448b00..f5cd51bf9cf 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -10,7 +10,7 @@ module QA
class << self
# Force top level group creation via UI if test is executed on dot_com environment
def fabricate!(*args, &prepare_block)
- if Specs::Helpers::ContextSelector.dot_com? || QA::Support::FIPS.enabled?
+ if Specs::Helpers::ContextSelector.dot_com? || Runtime::Env.personal_access_tokens_disabled?
return fabricate_via_browser_ui!(*args, &prepare_block)
end
diff --git a/qa/qa/resource/ssh_key.rb b/qa/qa/resource/ssh_key.rb
index dd475d7fa66..1c142058908 100644
--- a/qa/qa/resource/ssh_key.rb
+++ b/qa/qa/resource/ssh_key.rb
@@ -3,14 +3,12 @@
module QA
module Resource
class SSHKey < Base
- extend Forwardable
-
attr_reader :title
attr_accessor :expires_at
attribute :id
- def_delegators :key, :private_key, :public_key, :md5_fingerprint, :sha256_fingerprint
+ delegate :private_key, :public_key, :md5_fingerprint, :sha256_fingerprint, to: :key
def initialize
self.title = Time.now.to_f
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index c8babbc0b16..0398509396f 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -155,7 +155,7 @@ module QA
end
def self.fabricate_or_use(username = nil, password = nil)
- if Runtime::Env.signup_disabled? || !QA::Support::FIPS.enabled?
+ if Runtime::Env.signup_disabled? && !Runtime::Env.personal_access_tokens_disabled?
fabricate_via_api! do |user|
user.username = username
user.password = password
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
index 5ca3b0c51f8..213388ca264 100644
--- a/qa/qa/runtime/api/client.rb
+++ b/qa/qa/runtime/api/client.rb
@@ -35,9 +35,12 @@ module QA
end
def self.as_admin
- @admin_client ||= begin
+ @admin_client ||=
if Runtime::Env.admin_personal_access_token
- Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token)
+ Runtime::API::Client.new(
+ :gitlab,
+ personal_access_token: Runtime::Env.admin_personal_access_token
+ )
else
# To return an API client that has admin access, we need a user with admin access to confirm that
# the API client user has admin access.
@@ -62,7 +65,6 @@ module QA
client
end
- end
end
private
diff --git a/qa/qa/runtime/api/repository_storage_moves.rb b/qa/qa/runtime/api/repository_storage_moves.rb
index fb8d70c0836..450b7cd5712 100644
--- a/qa/qa/runtime/api/repository_storage_moves.rb
+++ b/qa/qa/runtime/api/repository_storage_moves.rb
@@ -16,7 +16,7 @@ module QA
QA::Runtime::Logger.debug("Move data: #{move}")
move[:state] == status &&
- move[:destination_storage_name] == destination_storage
+ move[:destination_storage_name] == destination_storage
end
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index d2ddaf86353..af1a4e06473 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -160,15 +160,14 @@ module QA
# From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326
Capybara::Screenshot.register_driver(QA::Runtime::Env.browser) do |driver, path|
+ QA::Runtime::Logger.info("Saving screenshot..")
driver.browser.save_screenshot(path)
end
- Capybara::Screenshot.append_timestamp = false
-
Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example|
::File.join(
QA::Runtime::Namespace.name(reset_cache: false),
- example.full_description.downcase.parameterize(separator: "_")[0..99]
+ example.full_description.downcase.parameterize(separator: "_")[0..79]
)
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 7cb7625118e..d4d9ffe62e6 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -133,6 +133,11 @@ module QA
enabled?(ENV['SIGNUP_DISABLED'], default: false)
end
+ # PATs are disabled for FedRamp
+ def personal_access_tokens_disabled?
+ enabled?(ENV['PERSONAL_ACCESS_TOKENS_DISABLED'], default: false)
+ end
+
def admin_password
ENV['GITLAB_ADMIN_PASSWORD']
end
@@ -431,7 +436,7 @@ module QA
end
def gitlab_agentk_version
- ENV.fetch('GITLAB_AGENTK_VERSION', 'v14.5.0')
+ ENV.fetch('GITLAB_AGENTK_VERSION', 'fe716ea')
end
def transient_trials
@@ -493,6 +498,10 @@ module QA
enabled?(ENV['QA_USE_PUBLIC_IP_API'], default: false)
end
+ def allow_local_requests?
+ enabled?(ENV['QA_ALLOW_LOCAL_REQUESTS'], default: false)
+ end
+
def chrome_default_download_path
ENV['DEFAULT_CHROME_DOWNLOAD_PATH']
end
diff --git a/qa/qa/runtime/ip_address.rb b/qa/qa/runtime/ip_address.rb
index fcb6db750bb..ae83d10ffb5 100644
--- a/qa/qa/runtime/ip_address.rb
+++ b/qa/qa/runtime/ip_address.rb
@@ -8,16 +8,17 @@ module QA
HostUnreachableError = Class.new(StandardError)
LOOPBACK_ADDRESS = '127.0.0.1'
- PUBLIC_IP_ADDRESS_API = "https://api.ipify.org"
+ PUBLIC_IP_ADDRESS_API = 'https://api.ipify.org'
def fetch_current_ip_address
# When running on CI against a live environment such as staging.gitlab.com,
# we use the public facing IP address
- non_test_host = !URI.parse(Scenario.gitlab_address).host.include?('.test')
+ non_test_host = !URI.parse(Scenario.gitlab_address).host.include?('.test') # rubocop:disable Rails/NegateInclude
has_no_public_ip = Env.running_in_ci? || Env.use_public_ip_api?
ip_address = if has_no_public_ip && non_test_host
- response = get(PUBLIC_IP_ADDRESS_API)
+ response = get_public_ip_address
+
raise HostUnreachableError, "#{PUBLIC_IP_ADDRESS_API} is unreachable" unless response.code == Support::API::HTTP_STATUS_OK
response.body
@@ -31,6 +32,12 @@ module QA
ip_address
end
+
+ def get_public_ip_address
+ Support::Retrier.retry_on_exception(sleep_interval: 1) do
+ get(PUBLIC_IP_ADDRESS_API)
+ end
+ end
end
end
end
diff --git a/qa/qa/runtime/logger.rb b/qa/qa/runtime/logger.rb
index e0e7385d6d4..7e78ba470d8 100644
--- a/qa/qa/runtime/logger.rb
+++ b/qa/qa/runtime/logger.rb
@@ -1,23 +1,21 @@
# frozen_string_literal: true
-require 'forwardable'
-
module QA
module Runtime
class Logger
- extend SingleForwardable
-
- def_delegators :logger, :debug, :info, :warn, :error, :fatal, :unknown
+ class << self
+ # Global logger instance
+ #
+ # @return [ActiveSupport::Logger]
+ def logger
+ @logger ||= Gitlab::QA::TestLogger.logger(
+ level: Gitlab::QA::Runtime::Env.log_level,
+ source: 'QA Tests',
+ path: File.expand_path('../../tmp', __dir__)
+ )
+ end
- # Global logger instance
- #
- # @return [ActiveSupport::Logger]
- def self.logger
- @logger ||= Gitlab::QA::TestLogger.logger(
- level: Gitlab::QA::Runtime::Env.log_level,
- source: 'QA Tests',
- path: File.expand_path('../../tmp', __dir__)
- )
+ delegate :debug, :info, :warn, :error, :fatal, :unknown, to: :logger
end
end
end
diff --git a/qa/qa/runtime/script_extensions/interceptor.js b/qa/qa/runtime/script_extensions/interceptor.js
index 9e98b0421b4..cde94e98774 100644
--- a/qa/qa/runtime/script_extensions/interceptor.js
+++ b/qa/qa/runtime/script_extensions/interceptor.js
@@ -101,6 +101,34 @@
}
/**
+ * @param url - the URL
+ * @param method - the REST method
+ * @param clonedResponse - a cloned fetch response
+ * @return {Promise<void>}
+ */
+ async function checkForGraphQLErrors(url, method, clonedResponse) {
+ if (/api\/graphql/.test(url)) {
+ const body = await clonedResponse.json();
+ if (body.errors && body.errors instanceof Array) {
+ const errorMessages = body.errors.map((error) => error.message);
+
+ commitToCache((cache) => {
+ // eslint-disable-next-line no-param-reassign
+ cache.errors ||= [];
+ cache.errors.push({
+ status: clonedResponse.status,
+ url,
+ method,
+ errorData: `error-messages: ${errorMessages.join(', ')}`,
+ headers: { 'x-request-id': clonedResponse.headers.get('x-request-id') },
+ });
+ return cache;
+ });
+ }
+ }
+ }
+
+ /**
* Replacement for fetch implementation
* tracks active requests, and commits metadata to the cache
* if the response is not ok or was cancelled.
@@ -115,7 +143,6 @@
window.Interceptor.activeFetchRequests += 1;
try {
const response = await pureFetch(url, opts, ...args);
- window.Interceptor.activeFetchRequests += -1;
const clone = response.clone();
if (!clone.ok) {
@@ -131,6 +158,9 @@
return cache;
});
}
+
+ await checkForGraphQLErrors(url, method, clone);
+
return response;
} catch (error) {
commitToCache((cache) => {
@@ -144,14 +174,24 @@
return cache;
});
- window.Interceptor.activeFetchRequests += -1;
throw error;
+ } finally {
+ window.Interceptor.activeFetchRequests += -1;
}
}
- if (checkCache()) {
- saveCache({});
- }
+ /**
+ * Initializes the cache
+ * if the cache doesn't already exist.
+ */
+ const initCache = () => {
+ if (checkCache() && getCache() == null) {
+ saveCache({});
+ }
+ };
+
+ // Initialize cache on page load.
+ initCache();
window.fetch = interceptedFetch;
window.XMLHttpRequest.prototype.open = interceptXhr;
diff --git a/qa/qa/scenario/test/integration/import.rb b/qa/qa/scenario/test/integration/import.rb
new file mode 100644
index 00000000000..4b0966998cd
--- /dev/null
+++ b/qa/qa/scenario/test/integration/import.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Integration
+ class Import < Test::Instance::All
+ tags :import
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/base.rb b/qa/qa/service/docker_run/base.rb
index c91f68d31a0..65ebe09eeea 100644
--- a/qa/qa/service/docker_run/base.rb
+++ b/qa/qa/service/docker_run/base.rb
@@ -77,6 +77,12 @@ module QA
def read_file(file_path)
`docker exec #{@name} /bin/cat #{file_path}`
end
+
+ def restart
+ return "Container #{@name} is not running, cannot restart." unless running?
+
+ shell "docker restart #{@name}"
+ end
end
end
end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
index 45ab4ceff99..7a62951a2f6 100644
--- a/qa/qa/service/docker_run/gitlab_runner.rb
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -56,6 +56,12 @@ module QA
@run_untagged = false
end
+ def restart
+ super
+
+ wait_until_shell_command_matches("docker logs #{@name}", /Configuration loaded/)
+ end
+
private
def register_command
diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb
index c332e7a6198..57f5310901b 100644
--- a/qa/qa/service/praefect_manager.rb
+++ b/qa/qa/service/praefect_manager.rb
@@ -9,6 +9,8 @@ module QA
attr_accessor :gitlab
+ attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres
+
PrometheusQueryError = Class.new(StandardError)
def initialize
@@ -21,7 +23,9 @@ module QA
@virtual_storage = 'default'
end
- attr_reader :primary_node, :secondary_node, :tertiary_node, :postgres
+ def gitaly_nodes
+ [primary_node, secondary_node, tertiary_node]
+ end
# Executes the praefect `dataloss` command.
#
@@ -50,42 +54,22 @@ module QA
end
end
- def stop_primary_node
- stop_node(@primary_node)
- wait_until_node_is_removed_from_healthy_storages(@primary_node)
- end
-
- def start_primary_node
- start_node(@primary_node)
- end
-
def start_praefect
start_node(@praefect)
- wait_for_praefect
+ QA::Runtime::Logger.info("Waiting for health check on praefect")
+ Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
+ wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line|
+ break true if line.include?('run: praefect: ')
+
+ QA::Runtime::Logger.debug(line.chomp)
+ end
+ end
end
def stop_praefect
stop_node(@praefect)
end
- def stop_secondary_node
- stop_node(@secondary_node)
- wait_until_node_is_removed_from_healthy_storages(@secondary_node)
- end
-
- def start_secondary_node
- start_node(@secondary_node)
- end
-
- def stop_tertiary_node
- stop_node(@tertiary_node)
- wait_until_node_is_removed_from_healthy_storages(@tertiary_node)
- end
-
- def start_tertiary_node
- start_node(@tertiary_node)
- end
-
def start_node(name)
state = node_state(name)
return if state == "running"
@@ -111,6 +95,8 @@ module QA
return if node_state(name) == 'paused'
shell "docker pause #{name}"
+
+ wait_until_node_is_removed_from_healthy_storages(name) if gitaly_nodes.include?(name)
end
def node_state(name)
@@ -126,9 +112,9 @@ module QA
QA::Runtime::Logger.info("Clearing the replication queue")
shell sql_to_docker_exec_cmd(
<<~SQL
- delete from replication_queue_job_lock;
- delete from replication_queue_lock;
- delete from replication_queue;
+ delete from replication_queue_job_lock;
+ delete from replication_queue_lock;
+ delete from replication_queue;
SQL
)
end
@@ -137,32 +123,16 @@ module QA
QA::Runtime::Logger.info("Setting jobs in replication queue to `in_progress` and acquiring locks")
shell sql_to_docker_exec_cmd(
<<~SQL
- update replication_queue set state = 'in_progress';
- insert into replication_queue_job_lock (job_id, lock_id, triggered_at)
- select id, rq.lock_id, created_at from replication_queue rq
- left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id
- where state = 'in_progress' and rqjl.job_id is null;
- update replication_queue_lock set acquired = 't';
+ update replication_queue set state = 'in_progress';
+ insert into replication_queue_job_lock (job_id, lock_id, triggered_at)
+ select id, rq.lock_id, created_at from replication_queue rq
+ left join replication_queue_job_lock rqjl on rq.id = rqjl.job_id
+ where state = 'in_progress' and rqjl.job_id is null;
+ update replication_queue_lock set acquired = 't';
SQL
)
end
- # Reconciles the previous primary node with the current one
- # I.e., it brings the previous primary node up-to-date
- def reconcile_nodes
- reconcile_node_with_node(@primary_node, current_primary_node)
- end
-
- def reconcile_node_with_node(target, reference)
- QA::Runtime::Logger.info("Reconcile #{target} with #{reference} on #{@virtual_storage}")
- wait_until_shell_command_matches(
- "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml reconcile -virtual #{@virtual_storage} -target #{target} -reference #{reference} -f'",
- /FINISHED: \d+ repos were checked for consistency/,
- sleep_interval: 5,
- retry_on_exception: true
- )
- end
-
def query_read_distribution
cmd = "docker exec #{@gitlab} bash -c 'curl -s http://localhost:9090/api/v1/query?query=gitaly_praefect_read_distribution'"
output = shell(cmd, stream_progress: false) do |line|
@@ -173,6 +143,8 @@ module QA
raise PrometheusQueryError, "Unable to query read distribution metrics" unless result['status'] == 'success'
+ raise PrometheusQueryError, "No read distribution metrics found" if result['data']['result'].empty?
+
result['data']['result'].map { |result| { node: result['metric']['storage'], value: result['value'][1].to_i } }
end
@@ -202,9 +174,7 @@ module QA
def start_all_nodes
start_postgres
- start_node(@primary_node)
- start_node(@secondary_node)
- start_node(@tertiary_node)
+ gitaly_nodes.each { |node| start_node(node) }
start_praefect
wait_for_health_check_all_nodes
@@ -228,17 +198,6 @@ module QA
destination_storage[:type] == :praefect ? verify_storage_move_to_praefect(repo_path, destination_storage[:name]) : verify_storage_move_to_gitaly(repo_path, destination_storage[:name])
end
- def wait_for_praefect
- QA::Runtime::Logger.info("Waiting for health check on praefect")
- Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
- wait_until_shell_command("docker exec #{@praefect} gitlab-ctl status praefect") do |line|
- break true if line.include?('run: praefect: ')
-
- QA::Runtime::Logger.debug(line.chomp)
- end
- end
- end
-
def praefect_sql_ping_healthy?
cmd = "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'"
wait_until_shell_command(cmd) do |line|
@@ -247,17 +206,6 @@ module QA
end
end
- def wait_for_sql_ping
- wait_until_shell_command_matches(
- "docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping'",
- /praefect sql-ping: OK/
- )
- end
-
- def health_check_failure_message?(msg)
- ['error when pinging healthcheck', 'failed checking node health'].include?(msg)
- end
-
def wait_for_dial_nodes_successful
Support::Waiter.repeat_until(max_attempts: 3, max_duration: 120, sleep_interval: 1) do
nodes_confirmed = {
@@ -314,14 +262,6 @@ module QA
dataloss_info
end
- def praefect_dataloss_info_for_project(project_id)
- dataloss_info = []
- Support::Retrier.retry_until(max_duration: 60) do
- dataloss_info = praefect_dataloss_information(project_id)
- dataloss_info.include?("#{Digest::SHA256.hexdigest(project_id.to_s)}.git")
- end
- end
-
def wait_for_project_synced_across_all_storages(project_id)
Support::Retrier.retry_until(max_duration: 60) do
praefect_dataloss_information(project_id).include?('All repositories are fully available on all assigned storages!')
@@ -345,9 +285,7 @@ module QA
end
def wait_for_health_check_all_nodes
- wait_for_gitaly_health_check(@primary_node)
- wait_for_gitaly_health_check(@secondary_node)
- wait_for_gitaly_health_check(@tertiary_node)
+ gitaly_nodes.each { |node| wait_for_gitaly_health_check(node) }
end
def wait_for_gitaly_health_check(node)
@@ -362,35 +300,11 @@ module QA
wait_until_node_is_marked_as_healthy_storage(node)
end
- def wait_for_primary_node_health_check
- wait_for_gitaly_health_check(@primary_node)
- end
-
- def wait_for_secondary_node_health_check
- wait_for_gitaly_health_check(@secondary_node)
- end
-
- def wait_for_tertiary_node_health_check
- wait_for_gitaly_health_check(@tertiary_node)
- end
-
def wait_for_health_check_failure(node)
QA::Runtime::Logger.info("Waiting for health check failure on #{node}")
wait_until_node_is_removed_from_healthy_storages(node)
end
- def wait_for_primary_node_health_check_failure
- wait_for_health_check_failure(@primary_node)
- end
-
- def wait_for_secondary_node_health_check_failure
- wait_for_health_check_failure(@secondary_node)
- end
-
- def wait_for_tertiary_node_health_check_failure
- wait_for_health_check_failure(@tertiary_node)
- end
-
def wait_until_node_is_removed_from_healthy_storages(node)
Support::Waiter.wait_until(max_duration: 120, sleep_interval: 1, raise_on_failure: true) do
result = []
@@ -457,10 +371,10 @@ module QA
result = []
shell sql_to_docker_exec_cmd(
<<~SQL
- select job from replication_queue
- where state = 'ready'
- and job ->> 'change' = 'update'
- and job ->> 'target_node_storage' = '#{@primary_node}';
+ select job from replication_queue
+ where state = 'ready'
+ and job ->> 'change' = 'update'
+ and job ->> 'target_node_storage' = '#{@primary_node}';
SQL
) do |line|
result << line
@@ -599,20 +513,6 @@ module QA
private
- def current_primary_node
- result = []
- shell sql_to_docker_exec_cmd("select node_name from shard_primaries where shard_name = '#{@virtual_storage}';") do |line|
- result << line
- end
- # The result looks like:
- # node_name
- # -----------
- # gitaly1
- # (1 row)
-
- result[2].strip
- end
-
def dataloss_command
"docker exec #{@praefect} bash -c '/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss'"
end
@@ -655,13 +555,6 @@ module QA
end
end
- def with_praefect_log(**kwargs)
- wait_until_shell_command("docker exec #{@praefect} bash -c 'tail -n 1 /var/log/gitlab/praefect/current'", **kwargs) do |line|
- QA::Runtime::Logger.debug(line.chomp)
- yield JSON.parse(line)
- end
- end
-
def repo_type(repo_path)
return :snippet if repo_path.start_with?('@snippets')
return :design if repo_path.end_with?('.design.git')
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
index 8bbef4ae429..9ffca8d54c9 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
@@ -25,8 +25,8 @@ module QA
it 'automatically fails over', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347830' do
# stop other nodes, so we can control which node the commit is sent to
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
@@ -40,8 +40,8 @@ module QA
# Stop the primary node to trigger failover, and then wait
# for Gitaly to be ready for writes again
- praefect_manager.stop_primary_node
- praefect_manager.wait_for_primary_node_health_check_failure
+ praefect_manager.stop_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
@@ -65,8 +65,8 @@ module QA
context 'when recovering from dataloss after failover' do
it 'automatically reconciles', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347831' do
# Start the old primary node again
- praefect_manager.start_primary_node
- praefect_manager.wait_for_primary_node_health_check
+ praefect_manager.start_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_gitaly_health_check(praefect_manager.primary_node)
# Confirm automatic reconciliation
expect(praefect_manager.replicated?(project.id)).to be true
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
index 1abc7b8a912..022f51205f0 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
@@ -27,8 +27,8 @@ module QA
# Stop the primary node to trigger failover, and then wait
# for Gitaly to be ready for writes again
- praefect_manager.stop_primary_node
- praefect_manager.wait_for_primary_node_health_check_failure
+ praefect_manager.stop_node(praefect_manager.primary_node)
+ praefect_manager.wait_for_health_check_failure(praefect_manager.primary_node)
# Push a commit to the new primary
Resource::Repository::ProjectPush.fabricate! do |push|
@@ -43,7 +43,7 @@ module QA
expect(praefect_manager).to be_replication_pending
# Start the old primary node again
- praefect_manager.start_primary_node
+ praefect_manager.start_node(praefect_manager.primary_node)
praefect_manager.wait_for_health_check_all_nodes
# Wait for automatic replication
@@ -51,8 +51,8 @@ module QA
# Force switch to the old primary node
# This ensures that the commit was replicated
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
# Confirm that both commits are available
expect(project.commits.map { |commit| commit[:message].chomp })
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
index 397fdb909ac..60ce2a65fd1 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
@@ -36,20 +36,16 @@ module QA
context 'when a node is unhealthy' do
before do
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
end
after do
# Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_secondary_node
+ praefect_manager.start_node(praefect_manager.secondary_node)
end
it 'does not read from the unhealthy node',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834',
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/378174',
- type: :flaky
- } do
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834' do
pre_read_data = praefect_manager.query_read_distribution
read_from_project(project, number_of_reads_per_loop * 10)
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
index 6ba192a9dd6..cf387c14006 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
@@ -29,7 +29,7 @@ module QA
praefect_manager.wait_for_project_synced_across_all_storages(project.id)
# testing for gitaly2 'out of sync'
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
number_of_changes = 3
1.upto(number_of_changes) do |i|
@@ -47,7 +47,7 @@ module QA
end
# testing for gitaly3 'in sync' but marked unhealthy
- praefect_manager.stop_tertiary_node
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
project_data_loss = praefect_manager.praefect_dataloss_information(project.id)
aggregate_failures "validate dataloss identified" do
@@ -74,7 +74,7 @@ module QA
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node)
- praefect_manager.stop_primary_node
+ praefect_manager.stop_node(praefect_manager.primary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-2'
@@ -86,7 +86,7 @@ module QA
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node)
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-3'
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
index 94bae38c5c8..f88372c0b59 100644
--- a/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
@@ -35,7 +35,7 @@ module QA
# During normal operations we avoid create a replication event
# https://gitlab.com/groups/gitlab-org/-/epics/7741
- praefect_manager.stop_secondary_node
+ praefect_manager.stop_node(praefect_manager.secondary_node)
Git::Repository.perform do |repository|
repository.uri = project.repository_http_location.uri
repository.use_default_credentials
@@ -47,7 +47,7 @@ module QA
end
repository.push_all_branches
end
- praefect_manager.start_secondary_node
+ praefect_manager.start_node(praefect_manager.secondary_node)
Support::Retrier.retry_until(max_duration: 60) do
count = praefect_manager.replication_queue_lock_count
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 ab50e02c790..27f9bcc9675 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
@@ -4,7 +4,7 @@ module QA
# https://github.com/gitlab-qa-github/import-test <- project under test
#
RSpec.describe 'Manage', product_group: :import do
- describe 'GitHub import', :reliable do
+ describe 'GitHub import' do
include_context 'with github import'
context 'when imported via api' do
diff --git a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
index 5acf15dd2b4..64ab8d8fc43 100644
--- a/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb
@@ -207,6 +207,8 @@ module QA
after do |example|
next unless defined?(@import_time)
+ # add additional import time metric
+ example.metadata[:custom_test_metrics] = { fields: { import_time: @import_time } }
# save data for comparison notification creation
save_json(
"data",
@@ -269,7 +271,7 @@ module QA
# fetch all objects right after import has started
fetch_github_objects
- import_status = lambda do
+ import_status = -> {
imported_project.project_import_status.yield_self do |status|
@stats = status.dig(:stats, :imported)
@@ -278,7 +280,7 @@ module QA
status[:import_status]
end
- end
+ }
logger.info("== Waiting for import to be finished ==")
expect(import_status).to eventually_eq('finished').within(max_duration: import_max_duration, sleep_interval: 30)
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
index e17e12cdaf3..1f0c37df101 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
@@ -1,70 +1,14 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :reliable, :requires_admin, product_group: :import do
- describe 'Gitlab migration' do
- let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
- let(:admin_api_client) { Runtime::API::Client.as_admin }
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
-
- let(:api_client) { Runtime::API::Client.new(user: user) }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "destination-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:source_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- group.avatar = File.new('qa/fixtures/designs/tanuki.jpg', 'r')
- end
- end
-
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = destination_group
- group.source_group = source_group
- end
- end
-
- let(:import_failures) do
- imported_group.import_details.sum([]) { |details| details[:failures] }
- end
-
- before do
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
- end
-
- after do |example|
- # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
- # Log failures for easier debugging
- Runtime::Logger.warn("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
- ensure
- user.remove_via_api!
- end
+ RSpec.describe "Manage", :reliable, product_group: :import do
+ include_context "with gitlab group migration"
+ describe "Gitlab migration" do
context 'with subgroups and labels' do
let(:subgroup) do
Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
+ group.api_client = source_admin_api_client
group.sandbox = source_group
group.path = "subgroup-for-import-#{SecureRandom.hex(4)}"
end
@@ -80,12 +24,12 @@ module QA
before do
Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
+ label.api_client = source_admin_api_client
label.group = source_group
label.title = "source-group-#{SecureRandom.hex(4)}"
end
Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
+ label.api_client = source_admin_api_client
label.group = subgroup
label.title = "subgroup-#{SecureRandom.hex(4)}"
end
@@ -97,7 +41,7 @@ module QA
'successfully imports groups and labels',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347674'
) do
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ expect_group_import_finished_successfully
aggregate_failures do
expect(imported_group.reload!).to eq(source_group)
@@ -112,7 +56,7 @@ module QA
context 'with milestones and badges' do
let(:source_milestone) do
Resource::GroupMilestone.fabricate_via_api! do |milestone|
- milestone.api_client = api_client
+ milestone.api_client = source_admin_api_client
milestone.group = source_group
end
end
@@ -121,7 +65,7 @@ module QA
source_milestone
Resource::GroupBadge.fabricate_via_api! do |badge|
- badge.api_client = api_client
+ badge.api_client = source_admin_api_client
badge.group = source_group
badge.link_url = "http://example.com/badge"
badge.image_url = "http://shields.io/badge"
@@ -134,7 +78,7 @@ module QA
'successfully imports group milestones and badges',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347628'
) do
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ expect_group_import_finished_successfully
imported_milestone = imported_group.reload!.milestones.find { |ml| ml.title == source_milestone.title }
aggregate_failures do
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 887eeda51e3..dd2e7f06995 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
@@ -7,12 +7,18 @@ module QA
let!(:source_issue) do
Resource::Issue.fabricate_via_api! do |issue|
- issue.api_client = api_client
+ issue.api_client = source_admin_api_client
issue.project = source_project
issue.labels = %w[label_one label_two]
end
end
+ let(:source_issue_comments) do
+ source_issue.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+
let(:imported_issues) { imported_projects.first.issues }
let(:imported_issue) do
@@ -24,6 +30,12 @@ module QA
end
end
+ let(:imported_issue_comments) do
+ imported_issue.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+
context 'with project issues' do
let!(:source_comment) { source_issue.add_comment(body: 'This is a test comment!') }
@@ -33,19 +45,18 @@ module QA
'successfully imports issue',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347608'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_issues.count).to eq(1)
-
- aggregate_failures do
- expect(imported_issue).to eq(source_issue.reload!)
-
- expect(imported_comments.count).to eq(1)
- expect(imported_comments.first&.fetch(:body)).to include(source_comment[:body])
- end
+ expect(imported_issue).to eq(source_issue.reload!)
+ expect(imported_issue_comments).to match_array(source_issue_comments)
end
end
- context "with designs" do
+ # 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
let!(:source_design) do
Flow::Login.sign_in(as: user)
@@ -66,7 +77,7 @@ module QA
'successfully imports design',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366449'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_issues.count).to eq(1)
expect(imported_design.full_path).to eq(source_design.full_path)
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
index 116a00f8385..5fc170435e3 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb
@@ -4,113 +4,76 @@
# rubocop:disable Rails/Pluck, Layout/LineLength, RSpec/MultipleMemoizedHelpers
module QA
- RSpec.describe "Manage", requires_admin: 'creates users', only: { job: 'large-gitlab-import' } do
- describe "Gitlab migration", product_group: :import do
- let(:logger) { Runtime::Logger.logger }
- let(:differ) { RSpec::Support::Differ.new(color: true) }
- let(:gitlab_group) { ENV['QA_LARGE_IMPORT_GROUP'] || 'gitlab-migration' }
- let(:gitlab_project) { ENV['QA_LARGE_IMPORT_REPO'] || 'dri' }
- let(:gitlab_source_address) { ENV['QA_LARGE_IMPORT_SOURCE_URL'] || 'https://staging.gitlab.com' }
-
- let(:import_wait_duration) do
- {
- max_duration: (ENV['QA_LARGE_IMPORT_DURATION'] || 3600).to_i,
- sleep_interval: 30
- }
- end
-
- let(:admin_api_client) { Runtime::API::Client.as_admin }
-
- # explicitly create PAT via api to not create it via UI in environments where admin token env var is not present
- let(:target_api_client) do
- Runtime::API::Client.new(
- user: user,
- personal_access_token: Resource::PersonalAccessToken.fabricate_via_api! do |pat|
- pat.api_client = admin_api_client
- end.token
- )
- end
-
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- end
- end
-
- let(:source_api_client) do
+ RSpec.describe "Manage", :skip_live_env, only: { job: "large-gitlab-import" } do
+ describe "Gitlab migration", orchestrated: false, product_group: :import do
+ include_context "with gitlab group migration"
+
+ let!(:logger) { Runtime::Logger.logger }
+ let!(:differ) { RSpec::Support::Differ.new(color: true) }
+ let!(:source_gitlab_address) { ENV["QA_LARGE_IMPORT_SOURCE_URL"] || "https://gitlab.com" }
+ let!(:gitlab_source_group) { ENV["QA_LARGE_IMPORT_GROUP"] || "gitlab-migration-large-import-test" }
+ let!(:gitlab_source_project) { ENV["QA_LARGE_IMPORT_REPO"] || "migration-test-project" }
+ let!(:import_wait_duration) { { max_duration: (ENV["QA_LARGE_IMPORT_DURATION"] || 3600).to_i, sleep_interval: 30 } }
+
+ let!(:source_admin_user) { "no-op" }
+ let!(:source_admin_api_client) do
Runtime::API::Client.new(
- gitlab_source_address,
- personal_access_token: ENV["QA_LARGE_IMPORT_GL_TOKEN"],
+ source_gitlab_address,
+ personal_access_token: ENV["QA_LARGE_IMPORT_GL_TOKEN"] || raise("missing QA_LARGE_IMPORT_GL_TOKEN variable"),
is_new_session: false
)
end
- let(:sandbox) do
+ # alias api client because for large import test it's not an actual admin user
+ let!(:source_api_client) { source_admin_api_client }
+
+ let!(:source_group) do
Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
+ group.api_client = source_api_client
+ group.path = gitlab_source_group
end
end
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
+ # generate unique target group because source group has a static name
+ let!(:target_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = admin_api_client
- group.sandbox = sandbox
- group.path = "imported-group-destination-#{SecureRandom.hex(4)}"
+ group.path = "qa-sandbox-#{SecureRandom.hex(4)}"
end
end
- # Source group and it's objects
+ # Source objects
#
- let(:source_group) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = source_api_client
- group.path = gitlab_group
- end
- end
-
- let(:source_project) { source_group.projects.find { |project| project.name.include?(gitlab_project) }.reload! }
+ let(:source_project) { source_group.projects.find { |project| project.name == gitlab_source_project }.reload! }
let(:source_branches) { source_project.repository_branches(auto_paginate: true).map { |b| b[:name] } }
let(:source_commits) { source_project.commits(auto_paginate: true).map { |c| c[:id] } }
let(:source_labels) { source_project.labels(auto_paginate: true).map { |l| l.except(:id) } }
let(:source_milestones) { source_project.milestones(auto_paginate: true).map { |ms| ms.except(:id, :web_url, :project_id) } }
let(:source_pipelines) { source_project.pipelines(auto_paginate: true).map { |pp| pp.except(:id, :web_url, :project_id) } }
- let(:source_mrs) { fetch_mrs(source_project, source_api_client) }
- let(:source_issues) { fetch_issues(source_project, source_api_client) }
+ let(:source_mrs) { fetch_mrs(source_project, source_api_client, transform_urls: true) }
+ let(:source_issues) { fetch_issues(source_project, source_api_client, transform_urls: true) }
- # Imported group and it's objects
+ # Imported objects
#
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.import_access_token = source_api_client.personal_access_token # token for importing on source instance
- group.api_client = target_api_client # token used by qa framework to access resources in destination instance
- group.gitlab_address = gitlab_source_address
- group.source_group = source_group
- group.sandbox = destination_group
- end
- end
-
- let(:imported_project) { imported_group.projects.find { |project| project.name.include?(gitlab_project) }.reload! }
+ let(:imported_project) { imported_group.projects.find { |project| project.name == gitlab_source_project }.reload! }
let(:branches) { imported_project.repository_branches(auto_paginate: true).map { |b| b[:name] } }
let(:commits) { imported_project.commits(auto_paginate: true).map { |c| c[:id] } }
let(:labels) { imported_project.labels(auto_paginate: true).map { |l| l.except(:id) } }
let(:milestones) { imported_project.milestones(auto_paginate: true).map { |ms| ms.except(:id, :web_url, :project_id) } }
- let(:pipelines) { imported_project.pipelines.map { |pp| pp.except(:id, :web_url, :project_id) } }
- let(:mrs) { fetch_mrs(imported_project, target_api_client) }
- let(:issues) { fetch_issues(imported_project, target_api_client) }
-
- let(:import_failures) { imported_group.import_details.sum([]) { |details| details[:failures] } }
+ let(:pipelines) { imported_project.pipelines(auto_paginate: true).map { |pp| pp.except(:id, :web_url, :project_id) } }
+ let(:mrs) { fetch_mrs(imported_project, api_client) }
+ let(:issues) { fetch_issues(imported_project, api_client) }
before do
- destination_group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects)
end
# rubocop:disable RSpec/InstanceVariable
after do |example|
- # Log failures for easier debugging
- Runtime::Logger.error("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
-
next unless defined?(@import_time)
+ # add additional import time metric
+ example.metadata[:custom_test_metrics] = { fields: { import_time: @import_time } }
# save data for comparison notification creation
save_json(
"data",
@@ -121,7 +84,7 @@ module QA
source: {
name: "GitLab Source",
project_name: source_project.path_with_namespace,
- address: gitlab_source_address,
+ address: source_gitlab_address,
data: {
branches: source_branches.length,
commits: source_commits.length,
@@ -163,17 +126,14 @@ module QA
start = Time.now
# trigger import and log imported group path
- logger.info("== Importing group '#{gitlab_group}' in to '#{imported_group.full_path}' ==")
+ logger.info("== Importing group '#{gitlab_source_group}' in to '#{imported_group.full_path}' ==")
# fetch all objects right after import has started
fetch_source_gitlab_objects
# wait for import to finish and save import time
logger.info("== Waiting for import to be finished ==")
- expect { imported_group.import_status }.not_to eventually_eq("started").within(import_wait_duration)
- # finished status actually means success, don't wait for finished status explicitly
- # because test would wait full duration if returned status is "failed"
- expect(imported_group.import_status).to eq("finished")
+ expect_group_import_finished_successfully
@import_time = Time.now - start
@@ -267,8 +227,8 @@ module QA
comment_diff = verify_comments(type, actual, expected)
{
- "missing_#{type}s": (expected.keys - actual.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
- "extra_#{type}s": (actual.keys - expected.keys).map { |it| expected[it]&.slice(:title, :url) }.compact,
+ "missing_#{type}s": (expected.keys - actual.keys).map { |it| expected[it]&.slice(:title, :url) }.compact,
+ "extra_#{type}s": (actual.keys - expected.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
"#{type}_comments": comment_diff
}
end
@@ -336,11 +296,12 @@ module QA
#
# @param [QA::Resource::Project]
# @param [Runtime::API::Client] client
+ # @param [Boolean] transform_urls
# @return [Hash]
- def fetch_mrs(project, client)
+ def fetch_mrs(project, client, transform_urls: false)
imported_mrs = project.merge_requests(auto_paginate: true, attempts: 2)
- Parallel.map(imported_mrs, in_threads: 4) do |mr|
+ Parallel.map(imported_mrs, in_threads: 6) do |mr|
resource = Resource::MergeRequest.init do |resource|
resource.project = project
resource.iid = mr[:iid]
@@ -350,11 +311,11 @@ module QA
[mr[:iid], {
url: mr[:web_url],
title: mr[:title],
- body: sanitize_description(mr[:description]) || '',
+ body: sanitize_description(mr[:description], transform_urls) || '',
state: mr[:state],
comments: resource
.comments(auto_paginate: true, attempts: 2)
- .map { |c| sanitize_comment(c[:body]) }
+ .map { |c| sanitize_comment(c[:body], transform_urls) }
}]
end.to_h
end
@@ -363,11 +324,12 @@ module QA
#
# @param [QA::Resource::Project]
# @param [Runtime::API::Client] client
+ # @param [Boolean] transform_urls
# @return [Hash]
- def fetch_issues(project, client)
+ def fetch_issues(project, client, transform_urls: false)
imported_issues = project.issues(auto_paginate: true, attempts: 2)
- Parallel.map(imported_issues, in_threads: 4) do |issue|
+ Parallel.map(imported_issues, in_threads: 6) do |issue|
resource = Resource::Issue.init do |issue_resource|
issue_resource.project = project
issue_resource.iid = issue[:iid]
@@ -378,42 +340,66 @@ module QA
url: issue[:web_url],
title: issue[:title],
state: issue[:state],
- body: sanitize_description(issue[:description]) || '',
+ body: sanitize_description(issue[:description], transform_urls) || '',
comments: resource
.comments(auto_paginate: true, attempts: 2)
- .map { |c| sanitize_comment(c[:body]) }
+ .map { |c| sanitize_comment(c[:body], transform_urls) }
}]
end.to_h
end
- # Importer user mention pattern
+ # Remove added postfixes and transform urls
#
- # @return [Regex]
- def created_by_pattern
- @created_by_pattern ||= /\n\n \*By #{importer_username_pattern} on \S+ \(imported from GitLab\)\*/
+ # Source urls need to be replaced with target urls for comparison to work
+ #
+ # @param [String] body
+ # @param [Boolean] transform_urls
+ # @return [String]
+ def sanitize_comment(body, transform_urls)
+ comment = body&.gsub(created_by_pattern, "")
+ return comment unless transform_urls
+
+ comment&.gsub(source_project_url, imported_project_url)
end
- # Username of importer user for removal from comments and descriptions
+ # Remove added postfixes and transform urls
+ #
+ # Source urls need to be replaced with target urls for comparison to work
#
+ # @param [String] body
+ # @param [Boolean] transform_urls
# @return [String]
- def importer_username_pattern
- @importer_username_pattern ||= ENV['QA_LARGE_IMPORT_USER_PATTERN'] || "(gitlab-migration|GitLab QA Bot)"
+ def sanitize_description(body, transform_urls)
+ description = body&.gsub(created_by_pattern, "")
+ return description unless transform_urls
+
+ description&.gsub(source_project_url, imported_project_url)
end
- # Remove added prefixes from comments
+ # Following objects are memoized via instance variables due to Parallel having some type of issue calling
+ # helpers defined via rspec let method
+
+ # Importer user mention pattern
+ #
+ # @return [Regex]
+ def created_by_pattern
+ @created_by_pattern ||= /\n\n \*By .+ on \S+ \(imported from GitLab\)\*/
+ end
+
+ # Source project url
#
- # @param [String] body
# @return [String]
- def sanitize_comment(body)
- body&.gsub(created_by_pattern, "")
+ def source_project_url
+ @source_group_url ||= "#{source_gitlab_address}/#{source_project.full_path}"
end
- # Remove created by prefix from descripion
+ # Imported project url
+ #
+ # This needs to be constructed manually because it is called before project import finishes
#
- # @param [String] body
# @return [String]
- def sanitize_description(body)
- body&.gsub(created_by_pattern, "")
+ def imported_project_url
+ @imported_group_url ||= "#{Runtime::Scenario.gitlab_address}/#{imported_group.full_path}/#{source_project.path}"
end
# Save json as file
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 07e54ead9c8..7fe11c3bafe 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
@@ -5,39 +5,37 @@ module QA
describe 'Gitlab migration', product_group: :import do
include_context 'with gitlab project migration'
- let(:member) do
+ let!(:source_member) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ end.tap(&:set_public_email)
+ end
+
+ let!(:target_member) do
Resource::User.fabricate_via_api! do |usr|
usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
+ usr.email = source_member.email
+ end.tap(&:set_public_email)
end
let(:imported_group_member) do
- imported_group.reload!.list_members.find { |usr| usr['username'] == 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'] == member.username }
- end
-
- before do
- member.set_public_email
- end
-
- after do
- member.remove_via_api!
+ imported_project.reload!.list_members.find { |usr| usr['username'] == target_member.username }
end
context 'with group member' do
before do
- source_group.add_member(member, Resource::Members::AccessLevel::DEVELOPER)
+ source_group.add_member(source_member, Resource::Members::AccessLevel::DEVELOPER)
end
it(
'member retains indirect membership in imported project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354416'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_project_member).to be_nil
@@ -50,14 +48,14 @@ module QA
context 'with project member' do
before do
- source_project.add_member(member, Resource::Members::AccessLevel::DEVELOPER)
+ source_project.add_member(source_member, Resource::Members::AccessLevel::DEVELOPER)
end
it(
'member retains direct membership in imported project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354417'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_group_member).to be_nil
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
index f44786939dc..127db36052f 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
@@ -7,57 +7,64 @@ module QA
let!(:source_project_with_readme) { true }
- # We create additional user so that object being migrated is not owned by the user doing migration
- let!(:other_user) do
- Resource::User
- .fabricate_via_api! { |usr| usr.api_client = admin_api_client }
- .tap do |usr|
- usr.set_public_email
- source_project.add_member(usr, Resource::Members::AccessLevel::MAINTAINER)
- end
+ let!(:source_mr_reviewer) do
+ reviewer = Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ usr.username = "source-reviewer-#{SecureRandom.hex(6)}"
+ end
+ reviewer.tap do |usr|
+ usr.set_public_email
+ source_project.add_member(usr, Resource::Members::AccessLevel::MAINTAINER)
+ end
end
let!(:source_mr) do
Resource::MergeRequest.fabricate_via_api! do |mr|
mr.project = source_project
- mr.api_client = Runtime::API::Client.new(user: other_user)
- mr.reviewer_ids = [other_user.id]
+ mr.api_client = source_admin_api_client
+ mr.reviewer_ids = [source_mr_reviewer.id]
end
end
- let!(:source_comment) { source_mr.add_comment(body: 'This is a test comment!') }
+ let!(:mr_reviewer) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.email = source_mr_reviewer.email
+ end.tap(&:set_public_email)
+ end
- let(:imported_mrs) { imported_project.merge_requests }
- let(:imported_mr_comments) { imported_mr.comments.map { |note| note.except(:id, :noteable_id) } }
- let(:source_mr_comments) { source_mr.comments.map { |note| note.except(:id, :noteable_id) } }
+ let!(:source_mr_reviewers) { [source_mr_reviewer.email] }
+ let!(:source_mr_approvers) { [source_admin_user.email] }
+ let(:source_mr_comments) do
+ source_mr.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+ let(:imported_mrs) { imported_project.merge_requests }
let(:imported_mr) do
Resource::MergeRequest.init do |mr|
mr.project = imported_project
- mr.iid = imported_mrs.first[:iid]
+ mr.iid = imported_project.merge_requests.first[:iid]
mr.api_client = api_client
end
end
- let(:imported_mr_reviewers) { imported_mr.reviewers.map { |r| r.slice(:name, :username) } }
- let(:source_mr_reviewers) { [{ name: other_user.name, username: other_user.username }] }
+ let(:imported_mr_comments) do
+ imported_mr.comments.map do |note|
+ { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) }
+ end
+ end
+ let(:imported_mr_reviewers) { imported_mr.reviewers.map { |reviewer| reviewer[:username] } }
let(:imported_mr_approvers) do
- imported_mr.approval_configuration[:approved_by].map do |usr|
- { username: usr.dig(:user, :username), name: usr.dig(:user, :name) }
- end
+ imported_mr.approval_configuration[:approved_by].map { |usr| usr.dig(:user, :username) }
end
before do
- source_project.update_approval_configuration(
- merge_requests_author_approval: true,
- approvals_before_merge: 1
- )
+ source_project.update_approval_configuration(merge_requests_author_approval: true, approvals_before_merge: 1)
source_mr.approve
- end
-
- after do
- other_user.remove_via_api!
+ source_mr.add_comment(body: 'This is a test comment!')
end
context 'with merge request' do
@@ -65,15 +72,15 @@ module QA
'successfully imports merge request',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348478'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_mrs.count).to eq(1)
aggregate_failures do
expect(imported_mr).to eq(source_mr.reload!)
expect(imported_mr_comments).to match_array(source_mr_comments)
- expect(imported_mr_reviewers).to eq(source_mr_reviewers)
- expect(imported_mr_approvers).to eq([{ username: other_user.username, name: other_user.name }])
+ expect(imported_mr_reviewers).to eq([mr_reviewer.username])
+ expect(imported_mr_approvers).to eq([source_admin_user.username])
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
index 7b79e6967c7..8d631808d17 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
@@ -22,7 +22,7 @@ module QA
before do
Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.api_client = api_client
+ commit.api_client = source_admin_api_client
commit.project = source_project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files(
@@ -47,7 +47,7 @@ module QA
'successfully imports ci pipeline',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354650'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_pipelines).to eq(source_pipelines)
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 2b7818d1ed2..83691cdf143 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
@@ -5,14 +5,54 @@ module QA
describe 'Gitlab migration', product_group: :import do
include_context 'with gitlab project migration'
+ # this spec is used as a sanity test for gitlab migration because it can run outside of orchestrated setup
+ context 'with import within same instance', orchestrated: false, import: false, quarantine: {
+ type: :test_environment,
+ issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/383605",
+ only: { job: "review-qa" }
+ } do
+ let!(:source_project_with_readme) { true }
+ let!(:source_gitlab_address) { Runtime::Scenario.gitlab_address }
+ let!(:source_admin_api_client) { admin_api_client }
+
+ let!(:source_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
+ group.api_client = admin_api_client
+ end
+ end
+
+ let!(:target_sandbox) { source_sandbox }
+
+ let!(:source_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ 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')
+ end
+ end
+
+ let(:destination_group_path) { "target-group-for-import-#{SecureRandom.hex(4)}" }
+ let(:cleanup!) { user.remove_via_api! }
+
+ it(
+ 'successfully imports project',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383351'
+ ) do
+ expect_project_import_finished_successfully
+
+ expect(imported_project).to eq(source_project)
+ end
+ end
+
context 'with uninitialized project' do
it(
'successfully imports project',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347610'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
- expect(imported_projects.first).to eq(source_project)
+ expect(imported_project).to eq(source_project)
end
end
@@ -59,7 +99,7 @@ module QA
'successfully imports repository',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347570'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
aggregate_failures do
expect(imported_commits).to match_array(source_commits)
@@ -78,9 +118,9 @@ module QA
'successfully imports project wiki',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347567'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
- expect(imported_projects.first.wikis).to eq(source_project.wikis)
+ expect(imported_project.wikis).to eq(source_project.wikis)
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
index 36036a2321e..b3510cef3e9 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_release_spec.rb
@@ -6,13 +6,13 @@ module QA
include_context 'with gitlab project migration'
context 'with release' do
- let(:tag) { 'v0.0.1' }
- let(:source_project_with_readme) { true }
+ let!(:tag) { 'v0.0.1' }
+ let!(:source_project_with_readme) { true }
- let(:milestone) do
+ let!(:milestone) do
Resource::ProjectMilestone.fabricate_via_api! do |resource|
resource.project = source_project
- resource.api_client = api_client
+ resource.api_client = source_admin_api_client
end
end
@@ -60,7 +60,7 @@ module QA
'successfully imports project release',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360243'
) do
- expect_import_finished
+ expect_project_import_finished_successfully
expect(imported_releases.size).to eq(1), "Expected to have 1 migrated release"
expect(imported_release).to eq(source_release)
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 16d4fd35b69..3df6e988bfa 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
@@ -70,13 +70,7 @@ module QA
it(
'is allowed to commit to sub-group project via the API',
- :reliable,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349',
- quarantine: {
- only: { subdomain: %i[staging staging-ref] },
- type: :investigating,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/370282'
- }
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349'
) do
expect do
Resource::Repository::Commit.fabricate_via_api! do |commit|
@@ -87,6 +81,9 @@ module QA
commit.commit_message = 'Add new file'
commit.add_files([{ file_path: 'test.txt', content: 'new file' }])
end
+ rescue StandardError => e
+ QA::Runtime::Logger.error("Full failure message: #{e.message}")
+ raise
end.not_to raise_error
end
diff --git a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
index 4ae97f589cf..6d375341c1b 100644
--- a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
+++ b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
+ RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383324',
+ type: :stale
+ } do
describe 'Pipeline with project file variables' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
diff --git a/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb b/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
index eb1b085c35c..7aaaa7137ed 100644
--- a/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
+++ b/qa/qa/specs/features/api/4_verify/remove_runner_spec.rb
@@ -17,17 +17,16 @@ module QA
# Removing a runner via the UI is covered by `spec/features/runners_spec.rb``
it 'removes the runner', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354828' do
- runners = nil
- expect { (runners = runner.list_of_runners(tag_list: runner_tags)).size }
- .to eventually_eq(1).within(max_duration: 10, sleep_interval: 1)
- expect(runners.first[:description]).to eq(executor)
+ runners_list = runner.runners_list
+ expect(runners_list.size).to eq(1)
+ expect(runners_list.first[:description]).to eq(executor)
- request = Runtime::API::Request.new(api_client, "runners/#{runners.first[:id]}")
+ request = Runtime::API::Request.new(api_client, "runners/#{runner.id}")
response = delete(request.url)
expect(response.code).to eq(Support::API::HTTP_STATUS_NO_CONTENT)
expect(response.body).to be_empty
- expect(runner.list_of_runners(tag_list: runner_tags)).to be_empty
+ expect(runner).to be_not_found_by_tags
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb
deleted file mode 100644
index c690202f091..00000000000
--- a/qa/qa/specs/features/browser_ui/1_manage/group/gitlab_migration_group_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- describe 'Manage', :requires_admin, :reliable, product_group: :import do
- describe 'Gitlab migration' do
- let!(:admin_api_client) { Runtime::API::Client.as_admin }
- let!(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
-
- let!(:api_client) { Runtime::API::Client.new(user: user) }
- let!(:personal_access_token) { api_client.personal_access_token }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:source_group) do
- Resource::Sandbox.fabricate! do |group|
- group.api_client = api_client
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:imported_group) do
- Resource::BulkImportGroup.init do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.source_group = source_group
- end
- end
-
- before do
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
-
- Flow::Login.sign_in(as: user)
-
- source_group
-
- Page::Main::Menu.perform(&:go_to_create_group)
- Page::Group::New.perform do |group|
- group.switch_to_import_tab
- group.connect_gitlab_instance(Runtime::Scenario.gitlab_address, personal_access_token)
- end
- end
-
- after do
- user.remove_via_api!
- end
-
- it(
- 'imports group from UI',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347862',
- issue_1: 'https://gitlab.com/gitlab-org/gitlab/-/issues/331252',
- issue_2: 'https://gitlab.com/gitlab-org/gitlab/-/issues/333678',
- issue_3: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332351',
- except: { job: 'instance-image-slow-network' }
- ) do
- Page::Group::BulkImport.perform do |import_page|
- import_page.import_group(imported_group.path, imported_group.sandbox.path)
-
- expect(import_page).to have_imported_group(imported_group.path, wait: 300)
-
- imported_group.reload!.visit!
- Page::Group::Show.perform do |group|
- expect(group).to have_content(imported_group.path)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb
new file mode 100644
index 00000000000..8fe4dc192bd
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Subgroup transfer', product_group: :workspace do
+ let(:source_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "source-group-for-transfer_#{SecureRandom.hex(8)}"
+ end
+ end
+
+ let!(:target_group) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "target-group-for-transfer_#{SecureRandom.hex(8)}"
+ end
+ end
+
+ let(:sub_group_for_transfer) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.path = "subgroup-for-transfer_#{SecureRandom.hex(8)}"
+ group.sandbox = source_group
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ sub_group_for_transfer.visit!
+ end
+
+ it 'transfers a subgroup to another group',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do
+ Page::Group::Menu.perform(&:click_group_general_settings_item)
+ Page::Group::Settings::General.perform do |general|
+ general.transfer_group(sub_group_for_transfer, target_group)
+
+ sub_group_for_transfer.sandbox = target_group
+ sub_group_for_transfer.reload!
+ end
+
+ expect(page).to have_text("Group '#{sub_group_for_transfer.path}' was successfully transferred.")
+ expect(page.driver.current_url).to include(sub_group_for_transfer.full_path)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
index 15563e3aa2a..43a8af93e27 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb
@@ -1,21 +1,11 @@
# frozen_string_literal: true
module QA
- # Spec uses real github.com, which means outage of github can actually block deployment
- # Keep spec in reliable bucket but don't run in blocking pipelines
- RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do
+ RSpec.describe 'Manage', product_group: :import do
describe 'GitHub import' do
- context 'when imported via UI' do
- let(:github_repo) { 'gitlab-qa-github/import-test' }
- let(:api_client) { Runtime::API::Client.as_admin }
- let(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
- let(:user) do
- Resource::User.fabricate_via_api! do |resource|
- resource.api_client = api_client
- resource.hard_delete_on_api_removal = true
- end
- end
+ include_context 'with github import'
+ context 'when imported via UI' do
let(:imported_project) do
Resource::ProjectImportedFromGithub.init do |project|
project.import = true
@@ -39,8 +29,6 @@ module QA
end
before do
- group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
-
Flow::Login.sign_in(as: user)
Page::Main::Menu.perform(&:go_to_create_project)
Page::Project::New.perform do |project_page|
@@ -49,10 +37,6 @@ module QA
end
end
- after do
- user.remove_via_api!
- end
-
it 'imports a project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do
Page::Project::Import::Github.perform do |import_page|
import_page.add_personal_access_token(Runtime::Env.github_access_token)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb
new file mode 100644
index 00000000000..4bcd2c44617
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/migration/gitlab_migration_group_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module QA
+ describe 'Manage', :reliable, product_group: :import do
+ describe 'Gitlab migration' do
+ include_context "with gitlab group migration"
+
+ let!(:imported_group) do
+ Resource::BulkImportGroup.init do |group|
+ group.api_client = api_client
+ group.sandbox = target_sandbox
+ group.source_group = source_group
+ end
+ end
+
+ before do
+ Flow::Login.sign_in(as: user)
+
+ Page::Main::Menu.perform(&:go_to_create_group)
+ Page::Group::New.perform do |group|
+ group.switch_to_import_tab
+ group.connect_gitlab_instance(source_gitlab_address, source_admin_api_client.personal_access_token)
+ end
+ end
+
+ it 'imports group from UI', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347862' do
+ Page::Group::BulkImport.perform do |import_page|
+ import_page.import_group(source_group.path, target_sandbox.path)
+
+ expect(import_page).to have_imported_group(imported_group.path, wait: 300)
+
+ imported_group.reload!.visit!
+ Page::Group::Show.perform do |group|
+ expect(group).to have_content(imported_group.path)
+ end
+ end
+ end
+ 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 66208921f0e..eaf43f04c4b 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
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:issue) { Resource::Issue.fabricate_via_api! }
let(:design_filename) { 'banana_sample.gif' }
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
index 8cbc6d7209c..03b2bc6823a 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:first_design) { Resource::Design.fabricate! }
diff --git a/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
index 8f4902026d2..61b67441ebb 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', quarantine: {
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366839',
- type: :test_environment,
- only: { job: 'review-qa-*' }
- } do
+ RSpec.describe 'Plan', product_group: :product_planning do
describe 'Design Management' do
let(:design) do
Resource::Design.fabricate_via_browser_ui! do |design|
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
index 36cfb9dfb6e..fd818c3797b 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
@@ -17,7 +17,7 @@ module QA
before do
Flow::Login.sign_in
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
# Ensure user exists
user
Flow::Login.sign_in_as_admin
@@ -31,7 +31,7 @@ module QA
project.add_member(user)
end
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.project = project
end.visit!
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index 330cae575e4..236af93716f 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -27,7 +27,7 @@ module QA
merge_request.visit!
Page::MergeRequest::Show.perform do |mr_page|
- expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.')
+ expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.', wait: 20)
expect(mr_page).to be_fast_forward_not_possible
expect(mr_page).not_to have_merge_button
expect(merge_request.project.commits.size).to eq(2)
diff --git a/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
index c35aa403bfa..449bffe61e0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
@@ -1,13 +1,21 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :gitlab_pages, :orchestrated, except: { job: 'review-qa-*', subdomain: :production } do
+ RSpec.describe 'Create',
+ :gitlab_pages,
+ :orchestrated,
+ except: { job: 'review-qa-*' },
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383215',
+ type: :test_environment,
+ only: { subdomain: 'staging-ref' }
+ } do
# TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300906
describe 'Pages', product_group: :editor do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
- project.name = 'jekyll-pages-project'
- project.template_name = :jekyll
+ project.name = 'gitlab-pages-project'
+ project.template_name = :plainhtml
end
end
@@ -45,7 +53,8 @@ module QA
pages.go_to_access_page
Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
retry_on_exception: true) do
- expect(page).to have_content('Write an awesome description for your new site here.')
+ expect(page).to have_content(
+ 'This is a simple plain-HTML website on GitLab Pages, without any fancy static site generator.')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
index 0503b1b3761..b98bb8592d3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
@@ -2,7 +2,10 @@
module QA
RSpec.describe 'Create' do
- describe 'Branch with unusual name', product_group: :source_code do
+ describe 'Branch with unusual name', product_group: :source_code, quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/364565',
+ type: :bug
+ } do
let(:branch_name) { 'unUsually/named#br--anch' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
@@ -29,6 +32,16 @@ module QA
Page::Project::Show.perform do |show|
show.switch_to_branch(branch_name)
+
+ # 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
+
+ raise("Console error(s):\n#{errors.join("\n\n")}") if errors.present?
+
show.click_file('test-folder')
expect(show).to have_file('test-file.md')
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
index 0e5fcea438d..aeb8e7d27bf 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb
@@ -18,7 +18,7 @@ module QA
end
after do
- if QA::Support::FIPS.enabled?
+ if Runtime::Env.personal_access_tokens_disabled?
snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:click_delete_button)
else
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
index 8ea65e17e13..93f804f1e39 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Web IDE file templates' do
include Runtime::Fixtures
@@ -11,6 +11,11 @@ module QA
project.description = 'Add file templates via the Web IDE'
project.initialize_with_readme = true
end
+ Runtime::Feature.disable(:vscode_web_ide, project: @project)
+ end
+
+ after(:all) do
+ Runtime::Feature.enable(:vscode_web_ide, project: @project)
end
templates = [
@@ -54,6 +59,7 @@ module QA
Page::Project::Show.perform(&:open_web_ide!)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.create_new_file_from_template template[:file_name], template[:name]
expect(ide.has_file?(template[:file_name])).to be_truthy
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
index 1da9ed652fe..a001dee891a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Add a directory in Web IDE' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -11,11 +11,15 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
project.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context 'when a directory with the same name already exists' do
let(:directory_name) { 'first_directory' }
@@ -38,6 +42,11 @@ module QA
it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347733' do
Page::Project::WebIDE::Edit.perform do |ide|
+ # Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ # retry_on_exception: true) do
+ # expect(ide).to have_element(:commit_mode_tab)
+ # end
+ ide.wait_until_ide_loads
ide.add_directory(directory_name)
end
@@ -54,6 +63,7 @@ module QA
it 'shows in the tree view but cannot be committed', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347732' do
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.add_directory(directory_name)
expect(ide).to have_file(directory_name)
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
index 1dfda1608f4..cb0da601a88 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'First file using Web IDE' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -13,14 +13,20 @@ module QA
let(:file_name) { 'the very first file.txt' }
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it "creates the first file in an empty project via Web IDE", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347803' do
project.visit!
Page::Project::Show.perform(&:create_first_new_file!)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.create_first_file(file_name)
ide.commit_changes
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
index 56cf2a08bd9..2007fe4a667 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Link to line in Web IDE' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:project) do
@@ -11,10 +11,12 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
end
after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
project.remove_via_api!
end
@@ -25,6 +27,7 @@ module QA
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.select_file('app.js')
@link = ide.link_line('26')
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
index 820b47a3175..dc9f68c5c73 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Open a fork in Web IDE',
skip: {
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/351696",
@@ -14,6 +14,14 @@ module QA
end
end
+ before do
+ Runtime::Feature.disable(:vscode_web_ide, project: parent_project)
+ end
+
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: parent_project)
+ end
+
context 'when a user does not have permissions to commit to the project' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
@@ -57,6 +65,7 @@ module QA
def submit_merge_request_upstream
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
expect(ide).to have_project_path("#{user.username}/#{parent_project.name}")
ide.add_file('new file', 'some random text')
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
index 685cd2d4ad6..039d25477bf 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Open Web IDE from Diff Tab' do
files = [
{
@@ -44,11 +44,15 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
merge_request.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it 'opens and edits a multi-file merge request in Web IDE from Diff Tab', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347724' do
Page::MergeRequest::Show.perform do |show|
show.click_diffs_tab
@@ -56,6 +60,7 @@ module QA
end
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
files.each do |files|
expect(ide).to have_file(files[:file_path])
expect(ide).to have_file_content(files[:file_path], files[:content])
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
index e4f29952f99..fe0060e9bbc 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb
@@ -1,31 +1,44 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381530', type: :stale } do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Review a merge request in Web IDE' do
let(:new_file) { 'awesome_new_file.txt' }
let(:original_text) { 'Text' }
let(:review_text) { 'Reviewed ' }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'review-merge-request-spec-project'
+ project.initialize_with_readme = true
+ end
+ end
+
let(:merge_request) do
Resource::MergeRequest.fabricate_via_api! do |mr|
mr.file_name = new_file
mr.file_content = original_text
+ mr.project = project
end
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
-
merge_request.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
it 'opens and edits a merge request in Web IDE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347786' do
Page::MergeRequest::Show.perform do |show|
show.click_open_in_web_ide
end
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.has_file?(new_file)
ide.add_to_modified_content(review_text)
ide.commit_changes
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 0972e4f3e3d..3cd14ecd799 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
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' }, product_group: :editor do
+ RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' },
+ feature_flag: { name: 'vscode_web_ide', scope: :project },
+ product_group: :editor do
describe 'Git Server Hooks' do
let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', 'README.md')) }
@@ -14,15 +16,21 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
project.visit!
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context '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
Page::Project::Show.perform(&:open_web_ide_via_shortcut)
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.upload_file(file_path)
ide.commit_changes(wait_for_success: false)
expect(ide).to have_text('Custom error message rejecting prereceive hook for projects with GL_PROJECT_PATH')
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 c0f65416a1c..c6e283f67e0 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,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', product_group: :editor do
+ RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :project }, product_group: :editor do
describe 'Upload a file in Web IDE' do
let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', file_name)) }
@@ -13,17 +13,23 @@ module QA
end
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
Flow::Login.sign_in
project.visit!
Page::Project::Show.perform(&:open_web_ide!)
end
+ after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
+ end
+
context 'when a file with the same name already exists' do
let(:file_name) { 'README.md' }
it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347850' do
Page::Project::WebIDE::Edit.perform do |ide|
+ ide.wait_until_ide_loads
ide.upload_file(file_path)
end
@@ -36,6 +42,7 @@ module QA
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)
expect(ide).to have_file(file_name)
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
index f90676ee15a..695b295bd86 100644
--- 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
@@ -10,10 +10,12 @@ module QA
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338179',
type: :bug
},
+ feature_flag: { name: 'vscode_web_ide', scope: :project },
product_group: :editor
) do
describe 'Web IDE web terminal' do
before do
+ Runtime::Feature.disable(:vscode_web_ide, project: project)
project = Resource::Project.fabricate_via_api! do |project|
project.name = 'web-terminal-project'
end
@@ -56,6 +58,7 @@ module QA
end
after do
+ Runtime::Feature.enable(:vscode_web_ide, project: project)
@runner.remove_via_api! if @runner
end
@@ -76,6 +79,7 @@ module QA
# 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
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
index 4a0a8be3659..5dda8b04805 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb
@@ -2,10 +2,7 @@
module QA
RSpec.describe 'Verify', :runner do
- describe 'Pipeline with customizable variable', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :project
- } do
+ describe 'Pipeline with customizable variable' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:pipeline_job_name) { 'customizable-variable' }
let(:variable_custom_value) { 'Custom Foo' }
@@ -48,74 +45,45 @@ module QA
end
end
- shared_examples 'pipeline with custom variable' do
- before do
- Flow::Login.sign_in
+ before do
+ Flow::Login.sign_in
- project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
-
- after do
- runner&.remove_via_api!
- end
-
- it 'manually creates a pipeline and uses the defined custom variable value' do
- Page::Project::Pipeline::New.perform do |new|
- new.configure_variable(value: variable_custom_value)
- new.click_run_pipeline_button
- end
-
- Page::Project::Pipeline::Show.perform do |show|
- Support::Waiter.wait_until { show.passed? }
- end
-
- job = Resource::Job.fabricate_via_api! do |job|
- job.id = project.job_by_name(pipeline_job_name)[:id]
- job.name = pipeline_job_name
- job.project = project
- end
-
- job.visit!
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
+ end
- Page::Project::Job::Show.perform do |show|
- expect(show.output).to have_content(variable_custom_value)
- end
- end
+ after do
+ runner&.remove_via_api!
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context(
- 'with feature flag disabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/361814'
- ) do
- before do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ it 'manually creates a pipeline and uses the defined custom variable value',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975' do
+ Page::Project::Pipeline::New.perform do |new|
+ new.configure_variable(value: variable_custom_value)
+ new.click_run_pipeline_button
end
- it_behaves_like 'pipeline with custom variable'
- end
-
- context(
- 'with feature flag enabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378975'
- ) do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql, project: project)
+ Page::Project::Pipeline::Show.perform do |show|
+ Support::Waiter.wait_until { show.passed? }
end
- after do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.id = project.job_by_name(pipeline_job_name)[:id]
+ job.name = pipeline_job_name
+ job.project = project
end
- it_behaves_like 'pipeline with custom variable'
+ job.visit!
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show.output).to have_content(variable_custom_value)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
index c4ce916d47d..1878292015e 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
@@ -2,13 +2,12 @@
module QA
RSpec.describe 'Verify' do
- describe 'Pipeline with prefill variables', feature_flag: {
- name: :run_pipeline_graphql,
- scope: :project
- } do
+ describe 'Pipeline with prefill variables' do
let(:prefill_variable_description1) { Faker::Lorem.sentence }
let(:prefill_variable_value1) { Faker::Lorem.word }
+ let(:prefill_variable_value5) { Faker::Lorem.word }
let(:prefill_variable_description2) { Faker::Lorem.sentence }
+ let(:prefill_variable_description5) { Faker::Lorem.sentence }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-prefill-variables'
@@ -33,7 +32,12 @@ module QA
TEST3:
value: test 3 value
TEST4: test 4 value
-
+ TEST5:
+ value: "FOO"
+ options:
+ - #{prefill_variable_value5}
+ - "FOO"
+ description: #{prefill_variable_description5}
test:
script: echo "$FOO"
YAML
@@ -43,62 +47,53 @@ module QA
end
end
- shared_examples 'pre-filled variables form' do
- before do
- Flow::Login.sign_in
+ before do
+ Flow::Login.sign_in
+ project.visit!
- project.visit!
- # Navigate to Run Pipeline page
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ # Navigate to Run Pipeline page
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
- # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
- # TODO: Investigate alternatives to deal with cache implementation
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
- page.refresh
- end
+ # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again.
+ # TODO: Investigate alternatives to deal with cache implementation
+ # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233
+ page.refresh
+ end
- it 'shows only variables with description as prefill variables on the run pipeline page' do
- Page::Project::Pipeline::New.perform do |new|
- aggregate_failures do
- expect(new).to have_field('Input variable key', with: 'TEST1')
- expect(new).to have_field('Input variable value', with: prefill_variable_value1)
- expect(new).to have_content(prefill_variable_description1)
+ it 'shows only variables with description as prefill variables on the run pipeline page',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new).to have_field('Input variable key', with: 'TEST1')
+ expect(new).to have_field('Input variable value', with: prefill_variable_value1)
+ expect(new).to have_content(prefill_variable_description1)
+
+ expect(new).to have_field('Input variable key', with: 'TEST2')
+ expect(new).to have_field('Input variable value', with: '')
+ expect(new).to have_content(prefill_variable_description2)
- expect(new).to have_field('Input variable key', with: 'TEST2')
- expect(new).to have_content(prefill_variable_description2)
+ expect(new).not_to have_field('Input variable key', with: 'TEST3')
+ expect(new).not_to have_field('Input variable key', with: 'TEST4')
- expect(new).not_to have_field('Input variable key', with: 'TEST3')
- expect(new).not_to have_field('Input variable key', with: 'TEST4')
- end
+ expect(new).to have_field('Input variable key', with: 'TEST5')
+ expect(new).to have_content(prefill_variable_description5)
end
end
end
- # TODO: Clean up tests when run_pipeline_graphql is enabled
- # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/372310
- context(
- 'with feature flag disabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371204'
- ) do
- before do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
- end
-
- it_behaves_like 'pre-filled variables form'
- end
+ it 'shows dropdown for variables with description, value, and options defined',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383820' do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new.variable_dropdown).to have_text('FOO')
- context 'with feature flag enabled',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/378977' do
- before do
- Runtime::Feature.enable(:run_pipeline_graphql, project: project)
- end
+ new.click_variable_dropdown
- after do
- Runtime::Feature.disable(:run_pipeline_graphql, project: project)
+ expect(new.variable_dropdown_item_with_index(0)).to have_text(prefill_variable_value5)
+ expect(new.variable_dropdown_item_with_index(1)).to have_text('FOO')
+ end
end
-
- it_behaves_like 'pre-filled variables form'
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
index fe934e8c60f..a4849d47183 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb
@@ -120,7 +120,7 @@ module QA
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/381486',
quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381806',
- only: { pipeline: %w[staging staging-canary] },
+ only: { pipeline: %w[staging staging-canary staging-ref] },
type: :waiting_on
} do
before 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
index e876bf3ab8b..448da6d9d87 100644
--- 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
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
- context 'When job is configured to only run on merge_request_events' 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)}" }
@@ -31,10 +31,12 @@ module QA
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"'
@@ -57,13 +59,17 @@ module QA
before do
Flow::Login.sign_in
- merge_request.visit!
- Page::MergeRequest::Show.perform(&:click_pipeline_link)
+ # 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!
- project.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
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
index b1ecce297c9..d30d5b43568 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
@@ -33,10 +33,7 @@ module QA
runner.remove_via_api!
end
- context(
- 'when policy is allowed',
- quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369397' }
- ) do
+ context 'when policy is allowed' do
let(:allowed_policies) { %w[if-not-present always never] }
where do
@@ -87,20 +84,19 @@ module QA
let(:allowed_policies) { %w[never] }
let(:pull_policies) { %w[always] }
- let(:message) do
- 'ERROR: Preparation failed: the configured PullPolicies ([always])'\
- ' are not allowed by AllowedPullPolicies ([never])'
- end
+ # The sentence seems differ from time to time,
+ # only checking portions of the sentence that matter
+ let(:text1) { 'pull_policy ([always])' }
+ let(:text2) { 'is not one of the allowed_pull_policies ([never])' }
it(
'fails job with policy not allowed message',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853',
- quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/371420', type: :stale }
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853'
) do
visit_job
- expect(job_log).to have_content(message),
- "Expected to find #{message} in #{job_log}, but didn't."
+ expect(job_log).to include(text1, text2),
+ "Expected to find contents #{text1} and #{text2} in #{job_log}, but didn't."
end
end
@@ -123,8 +119,7 @@ module QA
tempdir.close!
- # Give runner some time to pick up new configuration
- sleep(30)
+ runner.restart
end
def add_ci_file
@@ -154,7 +149,7 @@ module QA
def visit_job
Page::Project::Pipeline::Show.perform do |show|
- Support::Waiter.wait_until { show.completed? }
+ Support::Waiter.wait_until(max_duration: 90) { show.completed? }
show.click_job(job_name)
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
index aac8893ff2c..cda45efd828 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :skip_live_env, :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry do
describe 'Maven group level endpoint' do
include Runtime::Fixtures
+ include Support::Helpers::MaskToken
include_context 'packages registry qa scenario'
let(:group_id) { 'com.gitlab.qa' }
@@ -12,6 +13,18 @@ module QA
let(:package_version) { '1.3.7' }
let(:package_type) { 'maven' }
+ let(:group_deploy_token) do
+ Resource::GroupDeployToken.fabricate_via_api! do |deploy_token|
+ deploy_token.name = 'maven-group-deploy-token'
+ deploy_token.group = package_project.group
+ deploy_token.scopes = %w[
+ read_repository
+ read_package_registry
+ write_package_registry
+ ]
+ end
+ end
+
context 'via maven' do
where do
{
@@ -37,11 +50,13 @@ module QA
let(:token) do
case authentication_token_type
when :personal_access_token
- personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project)
when :ci_job_token
- '${env.CI_JOB_TOKEN}'
+ '${CI_JOB_TOKEN}'
when :project_deploy_token
- project_deploy_token.token
+ use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: package_project)
+ use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: client_project)
end
end
@@ -121,8 +136,9 @@ module QA
context 'duplication setting' do
before do
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project)
package_project.group.visit!
-
Page::Group::Menu.perform(&:go_to_package_settings)
end
@@ -132,16 +148,19 @@ module QA
end
it 'prevents users from publishing duplicates', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/377491' do
- create_duplicated_package
+ create_package(package_project)
+ show_latest_deploy_job
- push_duplicated_package
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 400)
+ end
- client_project.visit!
+ Page::Project::Job::Show.perform(&:retry!)
show_latest_deploy_job
Page::Project::Job::Show.perform do |job|
- expect(job).not_to be_successful(timeout: 800)
+ expect(job).not_to be_successful(timeout: 400)
end
end
end
@@ -152,52 +171,32 @@ module QA
end
it 'allows users to publish duplicates', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/377492' do
- create_duplicated_package
-
- push_duplicated_package
+ create_package(package_project)
show_latest_deploy_job
Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 800)
+ expect(job).to be_successful(timeout: 400)
end
- end
- end
- def create_duplicated_package
- settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding)
- pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding)
-
- with_fixtures([
- {
- file_path: 'pom.xml',
- content: pom_xml
- },
- {
- file_path: 'settings.xml',
- content: settings_xml_with_pat
- }
- ]) do |dir|
- Service::DockerRun::Maven.new(dir).publish!
- end
-
- package_project.visit!
+ Page::Project::Job::Show.perform(&:retry!)
- Page::Project::Menu.perform(&:click_packages_link)
+ show_latest_deploy_job
- Page::Project::Packages::Index.perform do |index|
- expect(index).to have_package(package_name)
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 400)
+ end
end
end
- def push_duplicated_package
+ def create_package(project)
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)
pom_xml = ERB.new(read_fixture('package_managers/maven/group/producer', 'pom.xml.erb')).result(binding)
settings_xml_with_pat = ERB.new(read_fixture('package_managers/maven/group', 'settings_with_pat.xml.erb')).result(binding)
- commit.project = client_project
+ commit.project = project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files(
[
@@ -210,7 +209,7 @@ module QA
end
def show_latest_deploy_job
- client_project.visit!
+ package_project.visit!
Flow::Pipeline.visit_latest_pipeline
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
index 8e1b0176f35..46c165ed806 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :skip_live_env, :orchestrated, :packages, :object_storage, :reliable,
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable,
feature_flag: {
name: 'maven_central_request_forwarding',
scope: :global
} do
describe 'Maven project level endpoint', product_group: :package_registry do
include Runtime::Fixtures
+ include Support::Helpers::MaskToken
let(:group_id) { 'com.gitlab.qa' }
let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" }
@@ -92,11 +93,11 @@ module QA
let(:token) do
case authentication_token_type
when :personal_access_token
- personal_access_token
+ use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project)
when :ci_job_token
- '${env.CI_JOB_TOKEN}'
+ '${CI_JOB_TOKEN}'
when :project_deploy_token
- project_deploy_token.token
+ use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: package_project)
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
index 63ab826e57b..284130fa92b 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/rubygems_registry_spec.rb
@@ -40,9 +40,6 @@ module QA
after do
Runtime::Feature.disable(:rubygem_packages, project: project)
- runner.remove_via_api!
- package.remove_via_api!
- project.remove_via_api!
end
it 'publishes a Ruby gem', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347649' do
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 057b4c15db1..d6446c9725d 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Configure', only: { subdomain: %i[staging staging-canary] }, product_group: :configure do
+ RSpec.describe 'Configure',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/381454', type: :flaky },
+ only: { subdomain: %i[staging staging-canary] }, product_group: :configure do
describe 'Auto DevOps with a Kubernetes Agent' do
let!(:app_project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep b/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/qa/qa/specs/features/browser_ui/8_monitor/.gitkeep
+++ /dev/null
diff --git a/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb
new file mode 100644
index 00000000000..8ea728ca94c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/http_endpoint_integration_creates_alert_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Monitor', product_group: :respond do
+ describe 'Http endpoint integration' do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-for-alerts'
+ project.description = 'Project for alerts'
+ end
+ end
+
+ let(:random_word) { Faker::Lorem.word }
+
+ let(:payload) do
+ { title: random_word, description: random_word }
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+ Flow::AlertSettings.setup_http_endpoint_and_send_alert(payload: payload)
+ end
+
+ it(
+ 'can send test alert that creates new alert',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/382803'
+ ) do
+ Page::Project::Menu.perform(&:go_to_monitor_alerts)
+ Page::Project::Monitor::Alerts::Index.perform do |alerts|
+ expect(alerts).to have_alert_with_title(random_word)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/sanity/interception_spec.rb b/qa/qa/specs/features/sanity/interception_spec.rb
new file mode 100644
index 00000000000..f8930db3aa5
--- /dev/null
+++ b/qa/qa/specs/features/sanity/interception_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Browser request interception', :orchestrated, :framework do
+ before(:context) do
+ skip 'Only can test for chrome' unless QA::Runtime::Env.can_intercept?
+ end
+
+ before do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ end
+
+ let(:page) { Capybara.current_session }
+ let(:logger) { class_double('QA::Runtime::Logger') }
+
+ it 'intercepts failed graphql calls' do
+ page.execute_script <<~JS
+ fetch('/api/graphql', {
+ method: 'POST',
+ body: JSON.stringify({ query: 'query {}'}),
+ headers: { 'Content-Type': 'application/json' }
+ })
+ JS
+
+ Support::Waiter.wait_until do
+ !get_cached_error.nil?
+ end
+ expect(**get_cached_error).to include({ 'method' => 'POST', 'status' => 200, 'url' => '/api/graphql' })
+ end
+
+ def get_cached_error
+ cache = page.execute_script <<~JS
+ return Interceptor.getCache()
+ JS
+
+ cache['errors']&.first
+ end
+ end
+end
diff --git a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
index 0a0c2a4a6df..27d94b04cde 100644
--- a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.shared_context "with github import", :github, :skip_live_env, :requires_admin, quarantine: {
- type: :broken,
- issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/382166"
- } do
+ RSpec.shared_context "with github import", :github, :import, :requires_admin, :orchestrated do
+ include QA::Support::Data::Github
+
+ let!(:github_repo) { "#{github_username}/import-test" }
let!(:api_client) { Runtime::API::Client.as_admin }
let!(:group) do
@@ -28,7 +28,7 @@ module QA
project.name = 'imported-project'
project.group = group
project.github_personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = 'gitlab-qa-github/import-test'
+ project.github_repository_path = github_repo
project.api_client = user_api_client
project.issue_events_import = true
project.full_notes_import = true
@@ -38,9 +38,5 @@ module QA
before do
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
end
-
- after do
- user.remove_via_api!
- 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
new file mode 100644
index 00000000000..e1d762f41cb
--- /dev/null
+++ b/qa/qa/specs/features/shared_contexts/import/gitlab_group_migration_common.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.shared_context(
+ 'with gitlab group migration',
+ :import,
+ :orchestrated,
+ requires_admin: 'creates a user via API'
+ ) do
+ let!(:import_wait_duration) { { max_duration: 120, sleep_interval: 2 } }
+
+ # source instance objects
+ #
+ let!(:source_gitlab_address) { ENV["QA_IMPORT_SOURCE_URL"] || raise("QA_IMPORT_SOURCE_URL is required!") }
+ let!(:source_admin_api_client) do
+ Runtime::API::Client.new(
+ source_gitlab_address,
+ personal_access_token: Runtime::Env.admin_personal_access_token || raise("Admin access token missing!"),
+ is_new_session: false
+ )
+ end
+ let!(:source_admin_user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = source_admin_api_client
+ usr.username = Runtime::Env.admin_username || "root"
+ end.tap(&:set_public_email)
+ end
+ let!(:source_group) do
+ 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")
+ end
+ end
+
+ # target instance objects
+ #
+ let!(:admin_api_client) { Runtime::API::Client.as_admin }
+ let!(:admin_user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.username = Runtime::Env.admin_username || "root"
+ end.tap(&:set_public_email)
+ end
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |usr|
+ usr.api_client = admin_api_client
+ usr.username = "target-user-#{SecureRandom.hex(6)}"
+ end
+ end
+ let!(:api_client) { Runtime::API::Client.new(user: user) }
+ let!(:target_sandbox) do
+ Resource::Sandbox.fabricate_via_api! do |group|
+ group.api_client = admin_api_client
+ end
+ end
+
+ let(:destination_group_path) { source_group.path }
+ let(:imported_group) do
+ Resource::BulkImportGroup.fabricate_via_api! do |group|
+ group.api_client = api_client
+ group.sandbox = target_sandbox
+ group.source_group = source_group
+ group.source_gitlab_address = source_gitlab_address
+ group.destination_group_path = destination_group_path
+ group.import_access_token = source_admin_api_client.personal_access_token
+ end
+ end
+
+ let(:import_failures) do
+ imported_group.import_details.sum([]) { |details| details[:failures] }
+ end
+
+ let(:cleanup!) {}
+
+ def expect_group_import_finished_successfully
+ imported_group # trigger import
+
+ status = nil
+ Support::Retrier.retry_until(**import_wait_duration, raise_on_failure: false) do
+ status = imported_group.import_status
+ %w[finished failed].include?(status)
+ end
+
+ # finished status means success, all other statuses are considered to fail the test
+ expect(status).to eq('finished'), "Expected import to finish successfully, but status was: #{status}"
+ end
+
+ before do
+ Runtime::ApplicationSettings.set_application_settings(bulk_import_enabled: true)
+
+ target_sandbox.add_member(user, Resource::Members::AccessLevel::OWNER)
+ end
+
+ after do |example|
+ # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
+ # Log failures for easier debugging
+ Runtime::Logger.error("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
+ rescue StandardError
+ # rescue when import did not happen at all and checking import failues will raise an error
+ ensure
+ # make sure cleanup runs last
+ cleanup!
+ end
+ end
+end
diff --git a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
index 9c80c088917..728907c708f 100644
--- a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
+++ b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb
@@ -1,91 +1,32 @@
# frozen_string_literal: true
module QA
- RSpec.shared_context 'with gitlab project migration', requires_admin: 'creates a user via API',
- quarantine: {
- type: :flaky,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/364839'
- },
- feature_flag: {
- name: 'bulk_import_projects',
- scope: :global
- } do
- let(:source_project_with_readme) { false }
- let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
- let(:admin_api_client) { Runtime::API::Client.as_admin }
- let(:user) do
- Resource::User.fabricate_via_api! do |usr|
- usr.api_client = admin_api_client
- usr.hard_delete_on_api_removal = true
- end
- end
+ RSpec.shared_context 'with gitlab project migration' do
+ # gitlab project migration doesn't work on just the projects
+ # so all project migration tests will always require setup for gitlab group migration
+ include_context "with gitlab group migration"
- let(:api_client) { Runtime::API::Client.new(user: user) }
-
- let(:sandbox) do
- Resource::Sandbox.fabricate_via_api! do |group|
- group.api_client = admin_api_client
- end
- end
-
- let(:destination_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = sandbox
- group.path = "destination-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:source_group) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
- end
- end
+ let(:source_project_with_readme) { false }
let(:source_project) do
Resource::Project.fabricate_via_api! do |project|
- project.api_client = api_client
+ project.api_client = source_admin_api_client
project.group = source_group
project.initialize_with_readme = source_project_with_readme
end
end
- let(:imported_group) do
- Resource::BulkImportGroup.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = destination_group
- group.source_group = source_group
- end
- end
-
let(:imported_projects) { imported_group.reload!.projects }
let(:imported_project) { imported_projects.first }
- let(:import_failures) do
- imported_group.import_details.sum([]) { |details| details[:failures] }
- end
-
- def expect_import_finished
- imported_group # trigger import
-
- expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+ def expect_project_import_finished_successfully
+ expect_group_import_finished_successfully
expect(imported_projects.count).to eq(1), "Expected to have 1 imported project. Found: #{imported_projects.count}"
end
before do
- Runtime::Feature.enable(:bulk_import_projects)
-
- sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects)
source_project # fabricate source group and project
end
-
- after do |example|
- # Checking for failures in the test currently makes test very flaky due to catching unrelated failures
- # Log failures for easier debugging
- Runtime::Logger.warn("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
- ensure
- user.remove_via_api!
- end
end
end
diff --git a/qa/qa/specs/helpers/feature_flag.rb b/qa/qa/specs/helpers/feature_flag.rb
index 7e618f19ed5..2b0f9e67a41 100644
--- a/qa/qa/specs/helpers/feature_flag.rb
+++ b/qa/qa/specs/helpers/feature_flag.rb
@@ -20,11 +20,11 @@ module QA
# This is to avoid flakiness with other tests running in parallel on the same environment
# as well as interfering with feature flag experimentation done by development groups.
example.metadata[:skip] = global_feature_flag_message if ContextSelector.dot_com?
- else
+ elsif skip_env_for_scoped_feature_flag
# Tests using a feature flag scoped to an actor (ex: :project, :user, :group), or
# with no scope defined (such as in the case of a low risk global feature flag),
# will only be skipped on environments without an admin account
- example.metadata[:skip] = feature_flag_message if skip_env_for_scoped_feature_flag
+ example.metadata[:skip] = feature_flag_message
end
end
end
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
index a4721040683..003e8aa74c1 100644
--- a/qa/qa/specs/spec_helper.rb
+++ b/qa/qa/specs/spec_helper.rb
@@ -52,12 +52,10 @@ RSpec.configure do |config|
end
config.prepend_after do |example|
- if example.exception
- page = Capybara.page
+ page = Capybara.page
+ QA::Support::PageErrorChecker.log_request_errors(page)
- QA::Support::PageErrorChecker.log_request_errors(page)
- QA::Support::PageErrorChecker.check_page_for_error_code(page)
- end
+ QA::Support::PageErrorChecker.check_page_for_error_code(page) if example.exception
end
# Add fabrication time to spec metadata
diff --git a/qa/qa/support/data/github.rb b/qa/qa/support/data/github.rb
new file mode 100644
index 00000000000..b0fe1baeff8
--- /dev/null
+++ b/qa/qa/support/data/github.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Data
+ module Github
+ def github_username
+ 'gitlab-qa-github'
+ end
+ end
+ end
+ end
+end
+
+QA::Support::Data::Github.prepend_mod_with('Support::Data::Github', namespace: QA)
diff --git a/qa/qa/support/data/license.rb b/qa/qa/support/data/license.rb
new file mode 100644
index 00000000000..cd4745fefcd
--- /dev/null
+++ b/qa/qa/support/data/license.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Data
+ module License
+ def license_user
+ 'GitLab QA'
+ end
+
+ def license_company
+ 'QA User'
+ end
+
+ def license_user_count
+ 10_000
+ end
+
+ def license_plan
+ QA::ULTIMATE_SELF_MANAGED
+ end
+ end
+ end
+ end
+end
+
+QA::Support::Data::License.prepend_mod_with('Support::Data::License', namespace: QA)
diff --git a/qa/qa/support/formatters/allure_metadata_formatter.rb b/qa/qa/support/formatters/allure_metadata_formatter.rb
index 02719536b17..c8ddbeb4536 100644
--- a/qa/qa/support/formatters/allure_metadata_formatter.rb
+++ b/qa/qa/support/formatters/allure_metadata_formatter.rb
@@ -116,8 +116,7 @@ module QA
# @return [Array]
def flaky_specs
@flaky_specs ||= influx_data.lazy.each_with_object({}) do |data, result|
- # Do not consider failures in same merge request
- records = data.records.reject { |r| r.values["_value"] == merge_request_iid }
+ records = data.records
runs = records.count
failed = records.count { |r| r.values["status"] == "failed" }
@@ -136,14 +135,14 @@ module QA
def influx_data
return [] unless run_type
- query_api.query(query: <<~QUERY).values
- from(bucket: "#{Support::InfluxdbTools::INFLUX_TEST_METRICS_BUCKET}")
- |> range(start: -14d)
+ query_api.query(query: <<~QUERY)
+ from(bucket: "#{Support::InfluxdbTools::INFLUX_MAIN_TEST_METRICS_BUCKET}")
+ |> range(start: -30d)
|> filter(fn: (r) => r._measurement == "test-stats")
|> filter(fn: (r) => r.run_type == "#{run_type}" and
r.status != "pending" and
r.quarantined == "false" and
- r._field == "merge_request_iid"
+ r._field == "id"
)
|> group(columns: ["testcase"])
QUERY
diff --git a/qa/qa/support/formatters/test_metrics_formatter.rb b/qa/qa/support/formatters/test_metrics_formatter.rb
index e84373a487d..6e6cdc35af5 100644
--- a/qa/qa/support/formatters/test_metrics_formatter.rb
+++ b/qa/qa/support/formatters/test_metrics_formatter.rb
@@ -8,6 +8,8 @@ module QA
class TestMetricsFormatter < RSpec::Core::Formatters::BaseFormatter
include Support::InfluxdbTools
+ CUSTOM_METRICS_KEY = :custom_test_metrics
+
RSpec::Core::Formatters.register(self, :stop)
# Finish test execution
@@ -19,16 +21,15 @@ module QA
parse_execution_data(notification.examples)
- if Runtime::Env.export_metrics?
- push_test_metrics
- push_fabrication_metrics
- end
-
- save_test_metrics if Runtime::Env.save_metrics_json?
+ push_test_metrics
+ push_fabrication_metrics
+ save_test_metrics
end
private
+ delegate :export_metrics?, :save_metrics_json?, :ci_job_url, :ci_job_name, to: "QA::Runtime::Env"
+
# Save execution data for the run
#
# @param [Array<RSpec::Core::Example>] examples
@@ -42,6 +43,8 @@ module QA
#
# @return [void]
def push_test_metrics
+ return log(:debug, "Metrics export not enabled, skipping test metrics export") unless export_metrics?
+
write_api.write(data: execution_data)
log(:debug, "Pushed #{execution_data.length} test execution entries to influxdb")
rescue StandardError => e
@@ -52,6 +55,8 @@ module QA
#
# @return [void]
def push_fabrication_metrics
+ return log(:debug, "Metrics export not enabled, skipping fabrication metrics export") unless export_metrics?
+
data = Tools::TestResourceDataProcessor.resources.flat_map do |resource, values|
values.map { |v| fabrication_stats(resource: resource, **v) }
end
@@ -67,6 +72,8 @@ module QA
#
# @return [void]
def save_test_metrics
+ return log(:debug, "Saving test metrics json not enabled, skipping") unless save_metrics_json?
+
File.write("tmp/test-metrics-#{env('CI_JOB_NAME_SLUG') || 'local'}.json", execution_data.to_json)
rescue StandardError => e
log(:error, "Failed to save test execution metrics, error: #{e}")
@@ -101,7 +108,8 @@ module QA
run_type: run_type,
stage: devops_stage(file_path),
product_group: example.metadata[:product_group],
- testcase: example.metadata[:testcase]
+ testcase: example.metadata[:testcase],
+ **custom_metrics_tags(example.metadata)
},
fields: {
id: example.id,
@@ -110,11 +118,12 @@ module QA
ui_fabrication: ui_fabrication,
total_fabrication: api_fabrication + ui_fabrication,
retry_attempts: retry_attempts(example.metadata),
- job_url: QA::Runtime::Env.ci_job_url,
+ job_url: ci_job_url,
pipeline_url: env('CI_PIPELINE_URL'),
pipeline_id: env('CI_PIPELINE_ID'),
job_id: env('CI_JOB_ID'),
- merge_request_iid: merge_request_iid
+ merge_request_iid: merge_request_iid,
+ **custom_metrics_fields(example.metadata)
}
}
rescue StandardError => e
@@ -139,13 +148,13 @@ module QA
resource: resource,
fabrication_method: fabrication_method,
http_method: http_method,
- run_type: env('QA_RUN_TYPE') || run_type,
+ run_type: run_type,
merge_request: merge_request
},
fields: {
fabrication_time: fabrication_time,
info: info,
- job_url: QA::Runtime::Env.ci_job_url,
+ job_url: ci_job_url,
timestamp: timestamp
}
}
@@ -155,7 +164,7 @@ module QA
#
# @return [String]
def job_name
- @job_name ||= QA::Runtime::Env.ci_job_name&.gsub(%r{ \d{1,2}/\d{1,2}}, '')
+ @job_name ||= ci_job_name&.gsub(%r{ \d{1,2}/\d{1,2}}, '')
end
# Single common timestamp for all exported example metrics to keep data points consistently grouped
@@ -220,6 +229,40 @@ module QA
metadata[:retry_attempts] || 0
end
+ # Additional custom metrics tags
+ #
+ # @param [Hash] metadata
+ # @return [Hash]
+ def custom_metrics_tags(metadata)
+ custom_metrics(metadata, :tags)
+ end
+
+ # Additional custom metrics fields
+ #
+ # @param [Hash] metadata
+ # @return [Hash]
+ def custom_metrics_fields(metadata)
+ custom_metrics(metadata, :fields)
+ end
+
+ # Custom test metrics
+ #
+ # @param [Hash] metadata
+ # @param [Symbol] type type of metric, :fields or :tags
+ # @return [Hash]
+ def custom_metrics(metadata, type)
+ custom_metrics = metadata[CUSTOM_METRICS_KEY]
+ return {} unless custom_metrics
+ return {} unless custom_metrics.is_a?(Hash) && custom_metrics[type].is_a?(Hash)
+
+ custom_metrics[type].to_h do |key, value|
+ k = key.to_sym
+ v = value.is_a?(Numeric) || value.nil? ? value : value.to_s
+
+ [k, v]
+ end
+ end
+
# Print log message
#
# @param [Symbol] level
diff --git a/qa/qa/support/helpers/mask_token.rb b/qa/qa/support/helpers/mask_token.rb
index 0c0af524c97..3aea77779ad 100644
--- a/qa/qa/support/helpers/mask_token.rb
+++ b/qa/qa/support/helpers/mask_token.rb
@@ -9,9 +9,8 @@ module QA
ci_variable.project = project
ci_variable.key = name
ci_variable.value = value
- ci_variable.protected = true
end
- "$#{name}"
+ "${#{name}}"
end
def use_group_ci_variable(name:, value:, group:)
@@ -19,9 +18,8 @@ module QA
ci_variable.group = group
ci_variable.key = name
ci_variable.value = value
- ci_variable.protected = true
end
- "$#{name}"
+ "${#{name}}"
end
end
end
diff --git a/qa/qa/support/influxdb_tools.rb b/qa/qa/support/influxdb_tools.rb
index e817b096864..efdbe1cd129 100644
--- a/qa/qa/support/influxdb_tools.rb
+++ b/qa/qa/support/influxdb_tools.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/module/delegation"
-
module QA
module Support
# Common tools for use with influxdb metrics setup
diff --git a/qa/qa/support/knapsack_report.rb b/qa/qa/support/knapsack_report.rb
index 659b8f10e0a..27d8b144f3b 100644
--- a/qa/qa/support/knapsack_report.rb
+++ b/qa/qa/support/knapsack_report.rb
@@ -5,13 +5,13 @@ require "fog/google"
module QA
module Support
class KnapsackReport
- extend SingleForwardable
-
PROJECT = "gitlab-qa-resources"
BUCKET = "knapsack-reports"
FALLBACK_REPORT = "knapsack/master_report.json"
- def_delegators :new, :configure!, :move_regenerated_report, :download_report, :upload_report
+ class << self
+ delegate :configure!, :move_regenerated_report, :download_report, :upload_report, to: :new
+ end
def initialize(report_name = nil)
@report_name = report_name
diff --git a/qa/qa/support/loglinking.rb b/qa/qa/support/loglinking.rb
index f24577ff313..5a1aad3c7eb 100644
--- a/qa/qa/support/loglinking.rb
+++ b/qa/qa/support/loglinking.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
+
+require 'active_support/core_ext/integer/time'
+
module QA
module Support
module Loglinking
@@ -7,63 +10,90 @@ module QA
STAGING_REF_ADDRESS = 'https://staging-ref.gitlab.com'
PRODUCTION_ADDRESS = 'https://gitlab.com'
PRE_PROD_ADDRESS = 'https://pre.gitlab.com'
- SENTRY_ENVIRONMENTS = {
+ SENTRY_BASE_URLS = {
staging: 'https://sentry.gitlab.net/gitlab/staginggitlabcom/?environment=gstg',
staging_ref: 'https://sentry.gitlab.net/gitlab/staging-ref/?environment=all',
pre: 'https://sentry.gitlab.net/gitlab/pregitlabcom/?environment=all',
production: 'https://sentry.gitlab.net/gitlab/gitlabcom/?environment=gprd'
}.freeze
- KIBANA_ENVIRONMENTS = {
+ KIBANA_BASE_URLS = {
staging: 'https://nonprod-log.gitlab.net/',
- canary: 'https://log.gprd.gitlab.net/',
- production: 'https://log.gprd.gitlab.net/'
+ production: 'https://log.gprd.gitlab.net/',
+ pre: 'https://nonprod-log.gitlab.net/'
+ }.freeze
+ KIBANA_INDICES = {
+ staging: 'ed942d00-5186-11ea-ad8a-f3610a492295',
+ production: '7092c4e2-4eb5-46f2-8305-a7da2edad090',
+ pre: 'pubsub-rails-inf-pre'
}.freeze
- def self.failure_metadata(correlation_id)
- return if correlation_id.blank?
+ class << self
+ def failure_metadata(correlation_id)
+ return if correlation_id.blank?
- sentry_uri = sentry_url
- kibana_uri = kibana_url
+ errors = ["Correlation Id: #{correlation_id}"]
- errors = ["Correlation Id: #{correlation_id}"]
- errors << "Sentry Url: #{sentry_uri}&query=correlation_id%3A%22#{correlation_id}%22" if sentry_uri
- errors << "Kibana Url: #{kibana_uri}app/discover#/?_a=%28query%3A%28language%3Akuery%2Cquery%3A%27json.correlation_id%20%3A%20#{correlation_id}%27%29%29&_g=%28time%3A%28from%3Anow-24h%2Cto%3Anow%29%29" if kibana_uri
+ env = get_logging_environment
- errors.join("\n")
- end
+ unless env.nil?
+ sentry_base_url = get_sentry_base_url(env)
+ kibana_base_url = get_kibana_base_url(env)
+ kibana_index = get_kibana_index(env)
- def self.sentry_url
- return unless logging_environment?
+ errors << "Sentry Url: #{get_sentry_url(sentry_base_url, correlation_id)}" if sentry_base_url
+ errors << "Kibana Url: #{get_kibana_url(kibana_base_url, kibana_index, correlation_id)}" if kibana_base_url
+ end
- SENTRY_ENVIRONMENTS[logging_environment]
- end
+ errors.join("\n")
+ end
- def self.kibana_url
- return unless logging_environment?
+ def get_sentry_base_url(env)
+ SENTRY_BASE_URLS[env]
+ end
- KIBANA_ENVIRONMENTS[logging_environment]
- end
+ def get_sentry_url(base_url, correlation_id)
+ "#{base_url}&query=correlation_id%3A%22#{correlation_id}%22"
+ end
- def self.logging_environment
- address = QA::Runtime::Scenario.attributes[:gitlab_address]
- return if address.nil?
-
- case address
- when STAGING_ADDRESS
- :staging
- when STAGING_REF_ADDRESS
- :staging_ref
- when PRODUCTION_ADDRESS
- :production
- when PRE_PROD_ADDRESS
- :pre
- else
- nil
+ def get_kibana_base_url(env)
+ KIBANA_BASE_URLS[env]
end
- end
- def self.logging_environment?
- !logging_environment.nil?
+ def get_kibana_index(env)
+ KIBANA_INDICES[env]
+ end
+
+ def get_kibana_url(base_url, index, correlation_id)
+ "#{base_url}app/discover#/?_a=%28index:%27#{index}%27%2Cquery%3A%28language%3Akuery%2C" \
+ "query%3A%27json.correlation_id%20%3A%20#{correlation_id}%27%29%29" \
+ "&_g=%28time%3A%28from%3A%27#{start_time}%27%2Cto%3A%27#{end_time}%27%29%29"
+ end
+
+ def get_logging_environment
+ address = QA::Runtime::Scenario.attributes[:gitlab_address]
+ return if address.nil?
+
+ case address
+ when STAGING_ADDRESS
+ :staging
+ when STAGING_REF_ADDRESS
+ :staging_ref
+ when PRODUCTION_ADDRESS
+ :production
+ when PRE_PROD_ADDRESS
+ :pre
+ else
+ nil
+ end
+ end
+
+ def start_time
+ (Time.now.utc - 24.hours).iso8601(3)
+ end
+
+ def end_time
+ Time.now.utc.iso8601(3)
+ end
end
end
end
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index 79ea4a8d001..2e97325aff0 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -79,9 +79,12 @@ module QA
super
end
+ # @param name [Symbol] name of the data_qa_selector element
+ # @param page [Class] a target page class to check existence of (class must inherit from QA::Page::Base)
+ # @param kwargs [Hash] keyword arguments to pass to Capybara finder
def click_element(name, page = nil, **kwargs)
msg = ["clicking :#{highlight_element(name)}"]
- msg << ", expecting to be at #{page.class}" if page
+ msg << "and ensuring #{page} is present" if page
log(msg.join(' '), :info)
log("with args #{kwargs}")
diff --git a/qa/qa/support/page_error_checker.rb b/qa/qa/support/page_error_checker.rb
index acba25643ae..1d791a83037 100644
--- a/qa/qa/support/page_error_checker.rb
+++ b/qa/qa/support/page_error_checker.rb
@@ -88,8 +88,8 @@ module QA
grouped_errors = group_errors(cache['errors'])
- errors = grouped_errors.map do |error_metadata, request_id_string|
- "#{error_metadata} -- #{request_id_string}"
+ errors = grouped_errors.map do |error_metadata, error_body|
+ "#{error_metadata} -- #{error_body[:request_id_string]}\n#{error_body[:error_body]}"
end
QA::Runtime::Logger.error "Interceptor Api Errors\n#{errors.join("\n")}" unless errors.nil? || errors.empty?
@@ -107,7 +107,7 @@ module QA
end
def logs(page)
- page.driver.browser.manage.logs.get(:browser)
+ page.driver.browser.logs.get(:browser)
end
private
@@ -120,7 +120,11 @@ module QA
errors.each_with_object({}) do |error, memo|
url = error['url']&.split('?')&.first || 'Unknown url'
key = "[#{error['status']}] #{error['method']} #{url}"
- memo[key] = "Correlation Id: #{error.dig('headers', 'x-request-id') || 'Correlation Id not found'}"
+ request_id_string = "Correlation Id: #{error.dig('headers', 'x-request-id') || 'Correlation Id not found'}"
+ memo[key] = {
+ request_id_string: request_id_string,
+ error_body: error['errorData']
+ }
end
end
end
diff --git a/qa/qa/support/run.rb b/qa/qa/support/run.rb
index 242293f9eef..da82c09462d 100644
--- a/qa/qa/support/run.rb
+++ b/qa/qa/support/run.rb
@@ -13,7 +13,7 @@ module QA
alias_method :to_s, :response
def success?
- exitstatus == 0 && !response.include?('Error encountered')
+ exitstatus == 0 && !response.include?('Error encountered') # rubocop:disable Rails/NegateInclude
end
def to_i
@@ -45,3 +45,5 @@ module QA
end
end
end
+
+QA::Support::Run.prepend_mod_with("Support::Run", namespace: QA)
diff --git a/qa/qa/support/ssh.rb b/qa/qa/support/ssh.rb
index 1b53244d1e4..eebe5e65504 100644
--- a/qa/qa/support/ssh.rb
+++ b/qa/qa/support/ssh.rb
@@ -70,3 +70,5 @@ module QA
end
end
end
+
+QA::Support::SSH.prepend_mod_with("Support::SSH", namespace: QA)
diff --git a/qa/qa/tools/long_running_spec_reporter.rb b/qa/qa/tools/long_running_spec_reporter.rb
index ce035248baa..865b16f1d41 100644
--- a/qa/qa/tools/long_running_spec_reporter.rb
+++ b/qa/qa/tools/long_running_spec_reporter.rb
@@ -6,15 +6,15 @@ require "slack-notifier"
module QA
module Tools
class LongRunningSpecReporter
- extend SingleForwardable
-
SLACK_CHANNEL = "#quality-reports"
PROJECT = "gitlab-qa-resources"
BUCKET = "knapsack-reports"
REPORT_NAME = "ee-instance-parallel.json"
RUNTIME_THRESHOLD = 300
- def_delegator :new, :execute
+ class << self
+ delegate :execute, to: :new
+ end
# Find and report specs exceeding runtime threshold
#
diff --git a/qa/qa/tools/reliable_report.rb b/qa/qa/tools/reliable_report.rb
index b3df6de3d54..fd39b637f83 100644
--- a/qa/qa/tools/reliable_report.rb
+++ b/qa/qa/tools/reliable_report.rb
@@ -326,7 +326,7 @@ module QA
def test_runs(reliable:)
puts("Fetching data on #{reliable ? 'reliable ' : ''}test execution for past #{range} days\n".colorize(:green))
- all_runs = query_api.query(query: query(reliable)).values
+ all_runs = query_api.query(query: query(reliable))
all_runs.each_with_object(Hash.new { |hsh, key| hsh[key] = {} }) do |table, result|
records = table.records.sort_by { |record| record.values["_time"] }
# skip specs that executed less time than defined by range or stopped executing before report date
@@ -341,7 +341,7 @@ module QA
runs = records.count
failed = records.count { |r| r.values["status"] == "failed" }
- failure_rate = (failed.to_f / runs.to_f) * 100
+ failure_rate = (failed.to_f / runs) * 100
result[stage][name] = {
file: file,
@@ -358,7 +358,7 @@ module QA
# @return [String]
def query(reliable)
<<~QUERY
- from(bucket: "#{Support::InfluxdbTools::INFLUX_TEST_METRICS_BUCKET}")
+ from(bucket: "#{Support::InfluxdbTools::INFLUX_MAIN_TEST_METRICS_BUCKET}")
|> range(start: -#{range}d)
|> filter(fn: (r) => r._measurement == "test-stats")
|> filter(fn: (r) => r.run_type == "staging-full" or
diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb
index 068fe37a37b..2aa8845605e 100644
--- a/qa/qa/tools/test_resources_handler.rb
+++ b/qa/qa/tools/test_resources_handler.rb
@@ -60,7 +60,9 @@ module QA
end
delete_resources(filtered_resources)
- delete_groups_permanently(filtered_resources['QA::Resource::Group'])
+
+ filtered_groups = filtered_resources['QA::Resource::Group']
+ delete_groups_permanently(filtered_groups) unless filtered_groups.nil?
end
return puts "\nDone" if failures.empty?
diff --git a/qa/qa/vendor/smocker/event_payload.rb b/qa/qa/vendor/smocker/event_payload.rb
index 4bf154b76c2..e7287c741ce 100644
--- a/qa/qa/vendor/smocker/event_payload.rb
+++ b/qa/qa/vendor/smocker/event_payload.rb
@@ -16,6 +16,10 @@ module QA
raw[:object_kind]&.to_sym
end
+ def event_name
+ raw[:event_name]&.to_sym
+ end
+
def project_name
raw.dig(:project, :name)
end
@@ -43,6 +47,14 @@ module QA
def wiki?
event == :wiki_page
end
+
+ def subgroup_create?
+ event_name == :subgroup_create
+ end
+
+ def subgroup_destroy?
+ event_name == :subgroup_destroy
+ end
end
end
end
diff --git a/qa/qa/vendor/smocker/smocker_api.rb b/qa/qa/vendor/smocker/smocker_api.rb
index 3f595b58886..230656776b7 100644
--- a/qa/qa/vendor/smocker/smocker_api.rb
+++ b/qa/qa/vendor/smocker/smocker_api.rb
@@ -31,6 +31,7 @@ module QA
def self.teardown!
@container&.remove!
+ @container = nil
end
def initialize(container, **wait_args)