summaryrefslogtreecommitdiff
path: root/qa/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa/qa')
-rw-r--r--qa/qa/git/repository.rb35
-rw-r--r--qa/qa/page/component/design_management.rb29
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb33
-rw-r--r--qa/qa/page/component/new_snippet.rb37
-rw-r--r--qa/qa/page/component/note.rb66
-rw-r--r--qa/qa/page/component/select2.rb2
-rw-r--r--qa/qa/page/component/snippet.rb42
-rw-r--r--qa/qa/page/dashboard/snippet/edit.rb4
-rw-r--r--qa/qa/page/merge_request/show.rb80
-rw-r--r--qa/qa/page/profile/ssh_keys.rb15
-rw-r--r--qa/qa/page/project/issue/show.rb12
-rw-r--r--qa/qa/page/project/operations/kubernetes/index.rb4
-rw-r--r--qa/qa/page/project/packages/index.rb2
-rw-r--r--qa/qa/page/project/pipeline/index.rb12
-rw-r--r--qa/qa/page/project/pipeline/new.rb19
-rw-r--r--qa/qa/page/project/show.rb2
-rw-r--r--qa/qa/page/project/snippet/index.rb29
-rw-r--r--qa/qa/resource/api_fabricator.rb35
-rw-r--r--qa/qa/resource/design.rb31
-rw-r--r--qa/qa/resource/events/base.rb7
-rw-r--r--qa/qa/resource/events/project.rb7
-rw-r--r--qa/qa/resource/merge_request.rb46
-rw-r--r--qa/qa/resource/project.rb16
-rw-r--r--qa/qa/resource/repository/push.rb5
-rw-r--r--qa/qa/resource/snippet.rb11
-rw-r--r--qa/qa/resource/ssh_key.rb9
-rw-r--r--qa/qa/runtime/api/request.rb6
-rw-r--r--qa/qa/runtime/feature.rb169
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb4
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb64
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb124
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb45
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb53
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb49
-rw-r--r--qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb4
-rw-r--r--qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb51
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb80
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb48
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb65
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb2
-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/sanity/framework_spec.rb2
-rw-r--r--qa/qa/support/api.rb2
-rw-r--r--qa/qa/support/wait_for_requests.rb12
61 files changed, 1195 insertions, 230 deletions
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 035946b8471..512653e8c4a 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -59,6 +59,10 @@ module QA
self.username, self.password = default_credentials
end
+ def use_default_identity
+ configure_identity('GitLab QA', 'root@gitlab.com')
+ end
+
def clone(opts = '')
clone_result = run("git clone #{opts} #{uri} ./", max_attempts: 3)
return clone_result.response unless clone_result.success?
@@ -122,8 +126,12 @@ module QA
run("git rev-parse --abbrev-ref HEAD").to_s
end
- def push_changes(branch = 'master')
- run("git push #{uri} #{branch}", max_attempts: 3).to_s
+ def push_changes(branch = 'master', push_options: nil)
+ cmd = ['git push']
+ cmd << push_options_hash_to_string(push_options)
+ cmd << uri
+ cmd << branch
+ run(cmd.compact.join(' '), max_attempts: 3).to_s
end
def push_all_branches
@@ -205,6 +213,10 @@ module QA
run("cat #{file}").to_s
end
+ def delete_netrc
+ File.delete(netrc_file_path) if File.exist?(netrc_file_path)
+ end
+
private
attr_reader :uri, :username, :password, :known_hosts_file,
@@ -216,7 +228,7 @@ module QA
alias_method :to_s, :response
def success?
- exitstatus == 0
+ exitstatus == 0 && !response.include?('Error encountered')
end
end
@@ -293,6 +305,23 @@ module QA
@tmp_home_dir ||= File.join(Dir.tmpdir, "qa-netrc-credentials", $$.to_s)
end
+ def push_options_hash_to_string(opts)
+ return if opts.nil?
+
+ prefix = "-o merge_request"
+ opts.each_with_object([]) do |(key, value), options|
+ if value.is_a?(Array)
+ value.each do |item|
+ options << "#{prefix}.#{key}=\"#{item}\""
+ end
+ elsif value == true
+ options << "#{prefix}.#{key}"
+ else
+ options << "#{prefix}.#{key}=\"#{value}\""
+ end
+ end.join(' ')
+ end
+
def netrc_file_path
@netrc_file_path ||= File.join(tmp_home_dir, '.netrc')
end
diff --git a/qa/qa/page/component/design_management.rb b/qa/qa/page/component/design_management.rb
index a8a24bd3949..44d6b02ccd8 100644
--- a/qa/qa/page/component/design_management.rb
+++ b/qa/qa/page/component/design_management.rb
@@ -31,6 +31,16 @@ module QA
element :design_file_name
element :design_image
end
+
+ view 'app/assets/javascripts/design_management/pages/index.vue' do
+ element :archive_button
+ element :design_checkbox
+ element :design_dropzone_content
+ end
+
+ view 'app/assets/javascripts/design_management/components/delete_button.vue' do
+ element :confirm_archiving_button
+ end
end
end
@@ -52,12 +62,14 @@ module QA
# It accepts a `class:` option, but that only works for class attributes
# It doesn't work as a CSS selector.
# So instead we use the name attribute as a locator
- page.attach_file("design_file", design_file_path, make_visible: { display: 'block' })
+ within_element(:design_dropzone_content) do
+ page.attach_file("design_file", design_file_path, make_visible: { display: 'block' })
+ end
filename = ::File.basename(design_file_path)
found = wait_until(reload: false, sleep_interval: 1) do
- image = find_element(:design_image)
+ image = find_element(:design_image, filename: filename)
has_element?(:design_file_name, text: filename) &&
image["complete"] &&
@@ -71,11 +83,24 @@ module QA
click_element(:design_file_name, text: filename)
end
+ def select_design(filename)
+ click_element(:design_checkbox, design: filename)
+ end
+
+ def archive_selected_designs
+ click_element(:archive_button)
+ click_element(:confirm_archiving_button)
+ end
+
def has_annotation?(note)
within_element_by_index(:design_discussion_content, 0) do
has_element?(:note_content, text: note)
end
end
+
+ def has_design?(filename)
+ has_element?(:design_file_name, text: filename)
+ end
end
end
end
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index 4e94049efe7..82347ee209a 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -18,16 +18,29 @@ module QA
element :more_assignees_link
end
- base.view 'app/helpers/dropdowns_helper.rb' do
+ base.view 'app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue' do
+ element :labels_block
+ end
+
+ base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue' do
+ element :selected_label_content
+ end
+
+ base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue' do
+ element :labels_dropdown_content
+ end
+
+ base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title.vue' do
+ element :labels_edit_button
+ end
+
+ base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue' do
element :dropdown_input_field
end
base.view 'app/views/shared/issuable/_sidebar.html.haml' do
element :assignee_block
- element :dropdown_menu_labels
- element :edit_labels_link
element :edit_milestone_link
- element :labels_block
element :milestone_block
element :milestone_link
end
@@ -64,7 +77,7 @@ module QA
def has_label?(label)
within_element(:labels_block) do
- !!has_element?(:label, label_name: label)
+ !!has_element?(:selected_label_content, label_name: label)
end
end
@@ -80,23 +93,25 @@ module QA
def select_labels_and_refresh(labels)
Support::Retrier.retry_until do
- click_element(:edit_labels_link)
- has_element?(:dropdown_menu_labels, text: labels.first)
+ click_element(:labels_edit_button)
+ has_element?(:labels_dropdown_content, text: labels.first)
end
labels.each do |label|
- within_element(:dropdown_menu_labels, text: label) do
+ within_element(:labels_dropdown_content) do
send_keys_to_element(:dropdown_input_field, [label, :enter])
end
end
- click_element(:edit_labels_link)
+ click_element(:labels_edit_button)
labels.each do |label|
has_element?(:labels_block, text: label, wait: 0)
end
refresh
+
+ wait_for_requests
end
def toggle_more_assignees_link
diff --git a/qa/qa/page/component/new_snippet.rb b/qa/qa/page/component/new_snippet.rb
index 3e5ae29177a..ff9ece9ad10 100644
--- a/qa/qa/page/component/new_snippet.rb
+++ b/qa/qa/page/component/new_snippet.rb
@@ -21,18 +21,11 @@ module QA
base.view 'app/assets/javascripts/snippets/components/snippet_blob_edit.vue' do
element :file_name_field
+ element :file_holder_container
end
- base.view 'app/views/shared/form_elements/_description.html.haml' do
- element :issuable_form_description
- end
-
- base.view 'app/views/shared/snippets/_form.html.haml' do
- element :snippet_description_field
- element :description_placeholder
- element :snippet_title_field
- element :file_name_field
- element :submit_button
+ base.view 'app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue' do
+ element :add_file_button
end
base.view 'app/views/shared/_zen.html.haml' do
@@ -54,12 +47,28 @@ module QA
choose visibility
end
- def fill_file_name(name)
- fill_element :file_name_field, name
+ def fill_file_name(name, file_number = nil)
+ if file_number
+ within_element_by_index(:file_holder_container, file_number - 1) do
+ fill_element(:file_name_field, name)
+ end
+ else
+ fill_element(:file_name_field, name)
+ end
+ end
+
+ def fill_file_content(content, file_number = nil)
+ if file_number
+ within_element_by_index(:file_holder_container, file_number - 1) do
+ text_area.set(content)
+ end
+ else
+ text_area.set content
+ end
end
- def fill_file_content(content)
- text_area.set content
+ def click_add_file
+ click_element(:add_file_button)
end
def click_create_snippet_button
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 0e9cdd49519..e6defd2ec0c 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -9,9 +9,18 @@ module QA
def self.included(base)
super
+ base.view 'app/assets/javascripts/diffs/components/diff_file_header.vue' do
+ element :toggle_comments_button
+ end
+
+ base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
+ element :discussion_reply_tab
+ element :resolve_discussion_button
+ end
+
base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
element :note_dropdown
- element :discussion_option
+ element :discussion_menu_item
end
base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
@@ -23,39 +32,32 @@ module QA
end
base.view 'app/assets/javascripts/notes/components/note_form.vue' do
- element :reply_input
+ element :reply_field
element :reply_comment_button
end
- base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
- element :discussion_reply_tab
- element :resolve_discussion_button
- end
-
base.view 'app/assets/javascripts/notes/components/toggle_replies_widget.vue' do
- element :expand_replies
- element :collapse_replies
+ element :expand_replies_button
+ element :collapse_replies_button
end
- base.view 'app/assets/javascripts/diffs/components/diff_file_header.vue' do
- element :toggle_comments_button
+ base.view 'app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue' do
+ element :skeleton_note_placeholder
end
end
- def start_discussion(text)
- fill_element :comment_input, text
- click_element :note_dropdown
- click_element :discussion_option
- click_element :comment_button
+ def collapse_replies
+ click_element :collapse_replies_button
end
- def toggle_comments(position)
- all_elements(:toggle_comments_button, minimum: position)[position - 1].click
+ def edit_comment(text)
+ click_element :note_edit_button
+ fill_element :reply_field, text
+ click_element :reply_comment_button
end
- def type_reply_to_discussion(position, reply_text)
- all_elements(:discussion_reply_tab, minimum: position)[position - 1].click
- fill_element :reply_input, reply_text
+ def expand_replies
+ click_element :expand_replies_button
end
def reply_to_discussion(position, reply_text)
@@ -69,18 +71,24 @@ module QA
end
end
- def collapse_replies
- click_element :collapse_replies
+ def start_discussion(text)
+ fill_element :comment_field, text
+ click_element :note_dropdown
+ click_element :discussion_menu_item
+ click_element :comment_button
end
- def expand_replies
- click_element :expand_replies
+ def toggle_comments(position)
+ all_elements(:toggle_comments_button, minimum: position)[position - 1].click
end
- def edit_comment(text)
- click_element :note_edit_button
- fill_element :reply_input, text
- click_element :reply_comment_button
+ def type_reply_to_discussion(position, reply_text)
+ all_elements(:discussion_reply_tab, minimum: position)[position - 1].click
+ fill_element :reply_field, reply_text
+ end
+
+ def wait_for_loading
+ has_no_element?(:skeleton_note_placeholer)
end
end
end
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
index 761bbb17168..87aed0105aa 100644
--- a/qa/qa/page/component/select2.rb
+++ b/qa/qa/page/component/select2.rb
@@ -43,6 +43,8 @@ module QA
end
def wait_for_search_to_complete
+ Support::WaitForRequests.wait_for_requests
+
has_css?('.select2-active', wait: 1)
has_no_css?('.select2-active', wait: 30)
end
diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb
index 2776b6c078e..7074d7e7649 100644
--- a/qa/qa/page/component/snippet.rb
+++ b/qa/qa/page/component/snippet.rb
@@ -98,15 +98,39 @@ module QA
end
end
- def has_file_name?(file_name)
- within_element(:file_title_content) do
- has_text?(file_name)
- end
- end
-
- def has_file_content?(file_content)
- within_element(:file_content) do
- has_text?(file_content)
+ def has_file_name?(file_name, file_number = nil)
+ if file_number
+ within_element_by_index(:file_title_content, file_number - 1) do
+ has_text?(file_name)
+ end
+ else
+ within_element(:file_title_content) do
+ has_text?(file_name)
+ end
+ end
+ end
+
+ def has_file_content?(file_content, file_number = nil)
+ if file_number
+ within_element_by_index(:file_content, file_number - 1) do
+ has_text?(file_content)
+ end
+ else
+ within_element(:file_content) do
+ has_text?(file_content)
+ end
+ end
+ end
+
+ def has_no_file_content?(file_content, file_number = nil)
+ if file_number
+ within_element_by_index(:file_content, file_number - 1) do
+ has_no_text?(file_content)
+ end
+ else
+ within_element(:file_content) do
+ has_no_text?(file_content)
+ end
end
end
diff --git a/qa/qa/page/dashboard/snippet/edit.rb b/qa/qa/page/dashboard/snippet/edit.rb
index 7802a680c25..31fc69a04cc 100644
--- a/qa/qa/page/dashboard/snippet/edit.rb
+++ b/qa/qa/page/dashboard/snippet/edit.rb
@@ -5,10 +5,6 @@ module QA
module Dashboard
module Snippet
class Edit < Page::Base
- view 'app/views/shared/snippets/_form.html.haml' do
- element :submit_button
- end
-
view 'app/assets/javascripts/snippets/components/edit.vue' do
element :submit_button
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 0b80ba84fa4..164f25389c0 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -44,15 +44,23 @@ module QA
element :squash_checkbox
end
- view 'app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue' do
- element :skeleton_note
- end
-
view 'app/views/projects/merge_requests/show.html.haml' do
element :notes_tab
element :diffs_tab
end
+ view 'app/assets/javascripts/diffs/components/compare_dropdown_layout.vue' do
+ element :dropdown_content
+ end
+
+ view 'app/assets/javascripts/diffs/components/compare_versions.vue' do
+ element :target_version_dropdown
+ end
+
+ view 'app/assets/javascripts/diffs/components/diff_file_header.vue' do
+ element :file_name_content
+ end
+
view 'app/assets/javascripts/diffs/components/inline_diff_table_row.vue' do
element :new_diff_line
end
@@ -67,15 +75,13 @@ module QA
view 'app/assets/javascripts/batch_comments/components/review_bar.vue' do
element :review_bar
- element :discard_review
- element :modal_delete_pending_comments
end
view 'app/assets/javascripts/notes/components/note_form.vue' do
element :unresolve_review_discussion_checkbox
element :resolve_review_discussion_checkbox
- element :start_review
- element :comment_now
+ element :start_review_button
+ element :comment_now_button
end
view 'app/assets/javascripts/batch_comments/components/preview_dropdown.vue' do
@@ -83,46 +89,54 @@ module QA
end
def start_review
- click_element :start_review
+ click_element(:start_review_button)
# After clicking the button, wait for it to disappear
# before moving on to the next part of the test
- has_no_element? :start_review
+ has_no_element?(:start_review_button)
+ end
+
+ def click_target_version_dropdown
+ click_element(:target_version_dropdown)
end
def comment_now
- click_element :comment_now
+ click_element(:comment_now_button)
# After clicking the button, wait for it to disappear
# before moving on to the next part of the test
- has_no_element? :comment_now
+ has_no_element?(:comment_now_button)
+ end
+
+ def version_dropdown_content
+ find_element(:dropdown_content).text
end
def submit_pending_reviews
- within_element :review_bar do
- click_element :review_preview_toggle
- click_element :submit_review
+ within_element(:review_bar) do
+ click_element(:review_preview_toggle)
+ click_element(:submit_review)
# After clicking the button, wait for it to disappear
# before moving on to the next part of the test
- has_no_element? :submit_review
+ has_no_element?(:submit_review)
end
end
def discard_pending_reviews
- within_element :review_bar do
- click_element :discard_review
+ within_element(:review_bar) do
+ click_element(:discard_review)
end
- click_element :modal_delete_pending_comments
+ click_element(:modal_delete_pending_comments)
end
def resolve_review_discussion
- scroll_to_element :start_review
- check_element :resolve_review_discussion_checkbox
+ scroll_to_element(:start_review_button)
+ check_element(:resolve_review_discussion_checkbox)
end
def unresolve_review_discussion
- check_element :unresolve_review_discussion_checkbox
+ check_element(:unresolve_review_discussion_checkbox)
end
def add_comment_to_diff(text)
@@ -131,7 +145,7 @@ module QA
end
all_elements(:new_diff_line, minimum: 1).first.hover
click_element(:diff_comment)
- fill_element(:reply_input, text)
+ fill_element(:reply_field, text)
end
def click_discussions_tab
@@ -160,6 +174,10 @@ module QA
has_no_text?('Fast-forward merge is not possible')
end
+ def has_file?(file_name)
+ has_element?(:file_name_content, text: file_name)
+ end
+
def has_merge_button?
refresh
@@ -168,7 +186,7 @@ module QA
def has_pipeline_status?(text)
# Pipelines can be slow, so we wait a bit longer than the usual 10 seconds
- has_element?(:merge_request_pipeline_info_content, text: text, wait: 30)
+ has_element?(:merge_request_pipeline_info_content, text: text, wait: 60)
end
def has_title?(title)
@@ -190,7 +208,7 @@ module QA
!find_element(:squash_checkbox).disabled?
end
- click_element :squash_checkbox
+ click_element(:squash_checkbox)
end
def merge!
@@ -202,7 +220,7 @@ module QA
end
def merged?
- has_element?(:merged_status_content, text: 'The changes were merged into', wait: 30)
+ has_element?(:merged_status_content, text: 'The changes were merged into', wait: 60)
end
# Check if the MR is able to be merged
@@ -235,7 +253,7 @@ module QA
!find_element(:mr_rebase_button).disabled?
end
- click_element :mr_rebase_button
+ click_element(:mr_rebase_button)
success = wait_until do
has_text?('Fast-forward merge without a merge commit')
@@ -251,12 +269,12 @@ module QA
end
def view_email_patches
- click_element :download_dropdown
+ click_element(:download_dropdown)
visit_link_in_element(:download_email_patches)
end
def view_plain_diff
- click_element :download_dropdown
+ click_element(:download_dropdown)
visit_link_in_element(:download_plain_diff)
end
@@ -266,10 +284,6 @@ module QA
end
end
- def wait_for_loading
- has_no_element?(:skeleton_note)
- end
-
def click_open_in_web_ide
click_element(:open_in_web_ide_button)
wait_for_requests
diff --git a/qa/qa/page/profile/ssh_keys.rb b/qa/qa/page/profile/ssh_keys.rb
index 810877e21ad..d27e113832c 100644
--- a/qa/qa/page/profile/ssh_keys.rb
+++ b/qa/qa/page/profile/ssh_keys.rb
@@ -11,8 +11,9 @@ module QA
element :add_key_button
end
- view 'app/views/profiles/keys/_key_details.html.haml' do
- element :delete_key_button
+ view 'app/helpers/profiles_helper.rb' do
+ element :delete_ssh_key_button
+ element :ssh_key_delete_modal
end
view 'app/views/profiles/keys/_key_table.html.haml' do
@@ -38,8 +39,14 @@ module QA
def remove_key(title)
click_link(title)
- accept_alert do
- click_element(:delete_key_button)
+ click_element(:delete_ssh_key_button)
+
+ # Retrying due to https://gitlab.com/gitlab-org/gitlab/-/issues/255287
+ retry_on_exception do
+ wait_for_animated_element(:ssh_key_delete_modal)
+ within_element(:ssh_key_delete_modal) do
+ click_button('Delete')
+ end
end
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 826acaa2e0a..a02617def9e 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -12,7 +12,7 @@ module QA
view 'app/assets/javascripts/notes/components/comment_form.vue' do
element :comment_button
- element :comment_input
+ element :comment_field
end
view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
@@ -43,7 +43,7 @@ module QA
end
view 'app/assets/javascripts/related_issues/components/related_issuable_input.vue' do
- element :add_issue_input
+ element :add_issue_field
end
view 'app/assets/javascripts/related_issues/components/related_issues_block.vue' do
@@ -57,8 +57,8 @@ module QA
def relate_issue(issue)
click_element(:related_issues_plus_button)
- fill_element(:add_issue_input, issue.web_url)
- send_keys_to_element(:add_issue_input, :enter)
+ fill_element(:add_issue_field, issue.web_url)
+ send_keys_to_element(:add_issue_field, :enter)
end
def related_issuable_item
@@ -84,7 +84,7 @@ module QA
# attachment option should be an absolute path
def comment(text, attachment: nil, filter: :all_activities)
method("select_#{filter}_filter").call
- fill_element :comment_input, "#{text}\n"
+ fill_element :comment_field, "#{text}\n"
unless attachment.nil?
QA::Page::Component::Dropzone.new(self, '.new-note')
@@ -125,6 +125,8 @@ module QA
click_element(:title)
click_element :discussion_filter
find_element(:filter_options, text: text).click
+
+ wait_for_loading
end
end
end
diff --git a/qa/qa/page/project/operations/kubernetes/index.rb b/qa/qa/page/project/operations/kubernetes/index.rb
index 0c92f9a9f28..114e3ddd46a 100644
--- a/qa/qa/page/project/operations/kubernetes/index.rb
+++ b/qa/qa/page/project/operations/kubernetes/index.rb
@@ -7,11 +7,11 @@ module QA
module Kubernetes
class Index < Page::Base
view 'app/views/clusters/clusters/_empty_state.html.haml' do
- element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add Kubernetes cluster')" # rubocop:disable QA/ElementWithPattern
+ element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Integrate with a cluster certificate')" # rubocop:disable QA/ElementWithPattern
end
def add_kubernetes_cluster
- click_on 'Add Kubernetes cluster'
+ click_on 'Connect cluster with certificate'
end
def has_cluster?(cluster)
diff --git a/qa/qa/page/project/packages/index.rb b/qa/qa/page/project/packages/index.rb
index 6d55d1d04b6..396d3373b8a 100644
--- a/qa/qa/page/project/packages/index.rb
+++ b/qa/qa/page/project/packages/index.rb
@@ -26,3 +26,5 @@ module QA
end
end
end
+
+QA::Page::Project::Packages::Index.prepend_if_ee('QA::EE::Page::Project::Packages::Index')
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index aa2ef2f058f..b2ecbb2e988 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -14,6 +14,10 @@ module QA
element :pipeline_retry_button
end
+ view 'app/assets/javascripts/pipelines/components/pipelines_list/nav_controls.vue' do
+ element :run_pipeline_button
+ end
+
def click_on_latest_pipeline
all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click
end
@@ -40,6 +44,14 @@ module QA
wait_for_latest_pipeline_success
end
end
+
+ def has_pipeline?
+ has_element? :pipeline_url_link
+ end
+
+ def click_run_pipeline_button
+ click_element :run_pipeline_button, Page::Project::Pipeline::New
+ end
end
end
end
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
new file mode 100644
index 00000000000..644a21b46e9
--- /dev/null
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Pipeline
+ class New < QA::Page::Base
+ view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
+ element :run_pipeline_button, required: true
+ end
+
+ def click_run_pipeline_button
+ click_element :run_pipeline_button
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index c607b35005e..d81be2803bd 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -103,6 +103,8 @@ module QA
end
def click_commit(commit_msg)
+ wait_for_requests
+
within_element(:file_tree_table) do
click_on commit_msg
end
diff --git a/qa/qa/page/project/snippet/index.rb b/qa/qa/page/project/snippet/index.rb
new file mode 100644
index 00000000000..a221abc4196
--- /dev/null
+++ b/qa/qa/page/project/snippet/index.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module Snippet
+ class Index < Page::Base
+ include Page::Component::Snippet
+
+ view 'app/views/shared/snippets/_snippet.html.haml' do
+ element :snippet_link
+ end
+
+ def has_project_snippet?(title)
+ has_element?(:snippet_link, snippet_title: title)
+ end
+
+ def click_snippet_link(title)
+ within_element(:snippet_link, text: title) do
+ click_link(title)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+QA::Page::Project::Snippet::Index.prepend_if_ee('QA::EE::Page::Project::Snippet::Index')
diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb
index d0cbab70983..034feb4e90f 100644
--- a/qa/qa/resource/api_fabricator.rb
+++ b/qa/qa/resource/api_fabricator.rb
@@ -96,15 +96,38 @@ module QA
end
def api_post
- response = post(
- Runtime::API::Request.new(api_client, api_post_path).url,
- api_post_body)
+ if api_post_path == "/graphql"
+ graphql_response = post(
+ Runtime::API::Request.new(api_client, api_post_path).url,
+ query: api_post_body)
- unless response.code == HTTP_STATUS_CREATED
- raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`."
+ flattened_response = flatten_hash(parse_body(graphql_response))
+
+ unless graphql_response.code == HTTP_STATUS_OK && flattened_response[:errors].empty?
+ raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{graphql_response.code}) with `#{graphql_response}`."
+ end
+
+ flattened_response[:web_url] = flattened_response.delete(:webUrl)
+ flattened_response[:id] = flattened_response.fetch(:id).split('/')[-1]
+
+ process_api_response(flattened_response)
+ else
+ response = post(
+ Runtime::API::Request.new(api_client, api_post_path).url,
+ api_post_body)
+
+ unless response.code == HTTP_STATUS_CREATED
+ raise ResourceFabricationFailedError, "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`."
+ end
+
+ process_api_response(parse_body(response))
end
+ end
- process_api_response(parse_body(response))
+ def flatten_hash(param)
+ param.each_pair.reduce({}) do |a, (k, v)|
+ v.is_a?(Hash) ? a.merge(flatten_hash(v)) : a.merge(k.to_sym => v)
+ end
end
def api_delete
diff --git a/qa/qa/resource/design.rb b/qa/qa/resource/design.rb
new file mode 100644
index 00000000000..10d4f32d49d
--- /dev/null
+++ b/qa/qa/resource/design.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Design < Base
+ attribute :issue do
+ Issue.fabricate_via_api!
+ end
+
+ attribute :filepath do
+ ::File.absolute_path(::File.join('spec', 'fixtures', @filename))
+ end
+
+ attribute :id
+ attribute :filename
+
+ def initialize
+ @filename = 'banana_sample.gif'
+ end
+
+ # TODO This will be replaced as soon as file uploads over GraphQL are implemented
+ def fabricate!
+ issue.visit!
+
+ Page::Project::Issue::Show.perform do |issue|
+ issue.add_design(filepath)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/resource/events/base.rb b/qa/qa/resource/events/base.rb
index 91ec0e59e37..4c5f54825b3 100644
--- a/qa/qa/resource/events/base.rb
+++ b/qa/qa/resource/events/base.rb
@@ -9,9 +9,12 @@ module QA
EventNotFoundError = Class.new(RuntimeError)
module Base
- def events(action: nil)
+ def events(action: nil, target_type: nil)
+ query = []
+ query << "action=#{CGI.escape(action)}" if action
+ query << "target_type=#{CGI.escape(target_type)}" if target_type
path = [api_get_events]
- path << "?action=#{CGI.escape(action)}" if action
+ path << "?#{query.join("&")}" unless query.empty?
parse_body(api_get_from("#{path.join}"))
end
diff --git a/qa/qa/resource/events/project.rb b/qa/qa/resource/events/project.rb
index 99c78254f42..8c97f66c663 100644
--- a/qa/qa/resource/events/project.rb
+++ b/qa/qa/resource/events/project.rb
@@ -6,6 +6,13 @@ module QA
module Project
include Events::Base
+ def wait_for_merge(title)
+ QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_merge with title "#{title}"])
+ wait_for_event do
+ events(action: 'accepted', target_type: 'merge_request').any? { |event| event[:target_title] == title }
+ end
+ end
+
def wait_for_push(commit_message)
QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_push with commit message "#{commit_message}"])
wait_for_event do
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index 6c0f4621dd9..dca8fb6dc6b 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'securerandom'
+require 'active_support/core_ext/object/blank'
module QA
module Resource
@@ -17,7 +18,12 @@ module QA
:labels,
:file_name,
:file_content
- attr_writer :no_preparation
+ attr_writer :no_preparation,
+ :wait_for_merge
+
+ attribute :merge_when_pipeline_succeeds
+ attribute :merge_status
+ attribute :state
attribute :project do
Project.fabricate! do |resource|
@@ -58,6 +64,7 @@ module QA
@file_content = "File Added"
@target_new_branch = true
@no_preparation = false
+ @wait_for_merge = true
end
def fabricate!
@@ -80,10 +87,17 @@ module QA
end
def fabricate_via_api!
+ resource_web_url(api_get)
+ rescue ResourceNotFoundError
populate(:target, :source) unless @no_preparation
+
super
end
+ def api_merge_path
+ "/projects/#{project.id}/merge_requests/#{id}/merge"
+ end
+
def api_get_path
"/projects/#{project.id}/merge_requests/#{id}"
end
@@ -100,6 +114,36 @@ module QA
title: @title
}
end
+
+ def merge_via_api!
+ Support::Waiter.wait_until(sleep_interval: 1) do
+ QA::Runtime::Logger.debug("Waiting until merge request with id '#{id}' can be merged")
+
+ reload!.api_resource[:merge_status] == 'can_be_merged'
+ end
+
+ Support::Retrier.retry_on_exception do
+ response = put(Runtime::API::Request.new(api_client, api_merge_path).url)
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceUpdateFailedError, "Could not merge. Request returned (#{response.code}): `#{response}`."
+ end
+
+ result = parse_body(response)
+
+ project.wait_for_merge(result[:title]) if @wait_for_merge
+
+ result
+ end
+ end
+
+ private
+
+ def transform_api_resource(api_resource)
+ raise ResourceNotFoundError if api_resource.blank?
+
+ super(api_resource)
+ end
end
end
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index 0025ebb2fd5..163c0b40bb5 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -110,6 +110,10 @@ module QA
response.any? { |file| file[:path] == file_path }
end
+ def has_branch?(branch)
+ has_branches?(Array(branch))
+ end
+
def has_branches?(branches)
branches.all? do |branch|
response = get(Runtime::API::Request.new(api_client, "#{api_repository_branches_path}/#{branch}").url)
@@ -140,6 +144,10 @@ module QA
"#{api_get_path}/members"
end
+ def api_merge_requests_path
+ "#{api_get_path}/merge_requests"
+ end
+
def api_runners_path
"#{api_get_path}/runners"
end
@@ -223,6 +231,14 @@ module QA
result[:import_status]
end
+ def merge_requests
+ parse_body(get(Runtime::API::Request.new(api_client, api_merge_requests_path).url))
+ end
+
+ def merge_request_with_title(title)
+ merge_requests.find { |mr| mr[:title] == title }
+ end
+
def runners(tag_list: nil)
response = if tag_list
get Runtime::API::Request.new(api_client, "#{api_runners_path}?tag_list=#{tag_list.compact.join(',')}").url
diff --git a/qa/qa/resource/repository/push.rb b/qa/qa/resource/repository/push.rb
index 1e5399fcc59..5266f8b9bea 100644
--- a/qa/qa/resource/repository/push.rb
+++ b/qa/qa/resource/repository/push.rb
@@ -11,7 +11,7 @@ module QA
:branch_name, :new_branch, :output, :repository_http_uri,
:repository_ssh_uri, :ssh_key, :user, :use_lfs, :tag_name
- attr_writer :remote_branch, :gpg_key_id
+ attr_writer :remote_branch, :gpg_key_id, :merge_request_push_options
def initialize
@file_name = "file-#{SecureRandom.hex(8)}.txt"
@@ -24,6 +24,7 @@ module QA
@use_lfs = false
@tag_name = nil
@gpg_key_id = nil
+ @merge_request_push_options = nil
end
def remote_branch
@@ -95,7 +96,7 @@ module QA
end
@output += commit_to repository
- @output += repository.push_changes("#{branch_name}:#{remote_branch}")
+ @output += repository.push_changes("#{branch_name}:#{remote_branch}", push_options: @merge_request_push_options)
end
repository.delete_ssh_key
diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb
index 39be5e5cb7d..c4ea6447209 100644
--- a/qa/qa/resource/snippet.rb
+++ b/qa/qa/resource/snippet.rb
@@ -11,6 +11,11 @@ module QA
@visibility = 'Public'
@file_content = 'The snippet content'
@file_name = 'New snippet file name'
+ @files = []
+ end
+
+ def add_files
+ yield @files
end
def fabricate!
@@ -22,6 +27,12 @@ module QA
new_page.set_visibility(@visibility)
new_page.fill_file_name(@file_name)
new_page.fill_file_content(@file_content)
+
+ @files.each.with_index(2) do |file, i|
+ new_page.click_add_file
+ new_page.fill_file_name(file[:name], i)
+ new_page.fill_file_content(file[:content], i)
+ end
new_page.click_create_snippet_button
end
end
diff --git a/qa/qa/resource/ssh_key.rb b/qa/qa/resource/ssh_key.rb
index d4e394954ce..fcd0a479fec 100644
--- a/qa/qa/resource/ssh_key.rb
+++ b/qa/qa/resource/ssh_key.rb
@@ -76,6 +76,15 @@ module QA
parse_body(response)[:title].include?(title)
end
end
+
+ private
+
+ def api_get
+ with_paginated_response_body(Runtime::API::Request.new(api_client, '/user/keys', per_page: '100').url) do |page|
+ key = page.find { |key| key[:title] == title }
+ break process_api_response(key) if key
+ end
+ end
end
end
end
diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb
index 724b499d32f..b58be354103 100644
--- a/qa/qa/runtime/api/request.rb
+++ b/qa/qa/runtime/api/request.rb
@@ -34,7 +34,11 @@ module QA
#
# Returns the relative path to the requested API resource
def request_path(path, version: API_VERSION, **query_string)
- full_path = ::File.join('/api', version, path)
+ full_path = if path == '/graphql'
+ ::File.join('/api', path)
+ else
+ ::File.join('/api', version, path)
+ end
if query_string.any?
full_path << (path.include?('?') ? '&' : '?')
diff --git a/qa/qa/runtime/feature.rb b/qa/qa/runtime/feature.rb
index 579c2293c51..a48bc216ac2 100644
--- a/qa/qa/runtime/feature.rb
+++ b/qa/qa/runtime/feature.rb
@@ -1,98 +1,125 @@
# frozen_string_literal: true
+require 'active_support/core_ext/object/blank'
+
module QA
module Runtime
- module Feature
- extend self
- extend Support::Api
+ class Feature
+ class << self
+ # Documentation: https://docs.gitlab.com/ee/api/features.html
- SetFeatureError = Class.new(RuntimeError)
- AuthorizationError = Class.new(RuntimeError)
+ include Support::Api
- def enable(key)
- QA::Runtime::Logger.info("Enabling feature: #{key}")
- set_feature(key, true)
- end
+ SetFeatureError = Class.new(RuntimeError)
+ AuthorizationError = Class.new(RuntimeError)
+ UnknownScopeError = Class.new(RuntimeError)
- def disable(key)
- QA::Runtime::Logger.info("Disabling feature: #{key}")
- set_feature(key, false)
- end
-
- def remove(key)
- request = Runtime::API::Request.new(api_client, "/features/#{key}")
- response = delete(request.url)
- unless response.code == QA::Support::Api::HTTP_STATUS_NO_CONTENT
- raise SetFeatureError, "Deleting feature flag #{key} failed with `#{response}`."
+ def remove(key)
+ request = Runtime::API::Request.new(api_client, "/features/#{key}")
+ response = delete(request.url)
+ unless response.code == QA::Support::Api::HTTP_STATUS_NO_CONTENT
+ raise SetFeatureError, "Deleting feature flag #{key} failed with `#{response}`."
+ end
end
- end
-
- def enable_and_verify(key)
- set_and_verify(key, enable: true)
- end
- def disable_and_verify(key)
- set_and_verify(key, enable: false)
- end
+ def enable(key, **scopes)
+ set_and_verify(key, enable: true, **scopes)
+ end
- def enabled?(key)
- feature = JSON.parse(get_features).find { |flag| flag["name"] == key }
- feature && feature["state"] == "on"
- end
+ def disable(key, **scopes)
+ set_and_verify(key, enable: false, **scopes)
+ end
- def get_features
- request = Runtime::API::Request.new(api_client, "/features")
- response = get(request.url)
- response.body
- end
+ def enabled?(key, **scopes)
+ feature = JSON.parse(get_features).find { |flag| flag['name'] == key.to_s }
+ feature && feature['state'] == 'on' || feature['state'] == 'conditional' && scopes.present? && enabled_scope?(feature['gates'], scopes)
+ end
- private
+ private
- def api_client
- @api_client ||= begin
- if Runtime::Env.admin_personal_access_token
- Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token)
- else
- user = Resource::User.fabricate_via_api! do |user|
- user.username = Runtime::User.admin_username
- user.password = Runtime::User.admin_password
- end
+ def api_client
+ @api_client ||= Runtime::API::Client.as_admin
+ rescue Runtime::API::Client::AuthorizationError => e
+ raise AuthorizationError, "Administrator access is required to enable/disable feature flags. #{e.message}"
+ end
- unless user.admin?
- raise AuthorizationError, "Administrator access is required to enable/disable feature flags. User '#{user.username}' is not an administrator."
+ def enabled_scope?(gates, scopes)
+ scopes.each do |key, value|
+ case key
+ when :project, :group, :user
+ actors = gates.filter { |i| i['key'] == 'actors' }.first['value']
+ break actors.include?("#{key.to_s.capitalize}:#{value.id}")
+ when :feature_group
+ groups = gates.filter { |i| i['key'] == 'groups' }.first['value']
+ break groups.include?(value)
+ else
+ raise UnknownScopeError, "Unknown scope: #{key}"
end
-
- Runtime::API::Client.new(:gitlab, user: user)
end
end
- end
- # Change a feature flag and verify that the change was successful
- # Arguments:
- # key: The feature flag to set (as a string)
- # enable: `true` to enable the flag, `false` to disable it
- def set_and_verify(key, enable:)
- Support::Retrier.retry_on_exception(sleep_interval: 2) do
- enable ? enable(key) : disable(key)
+ def get_features
+ request = Runtime::API::Request.new(api_client, '/features')
+ response = get(request.url)
+ response.body
+ end
- is_enabled = nil
+ # Change a feature flag and verify that the change was successful
+ # Arguments:
+ # key: The feature flag to set (as a string)
+ # enable: `true` to enable the flag, `false` to disable it
+ # scopes: Any scope (user, project, group) to restrict the change to
+ def set_and_verify(key, enable:, **scopes)
+ msg = "#{enable ? 'En' : 'Dis'}abling feature: #{key}"
+ msg += " for scope \"#{scopes_to_s(scopes)}\"" if scopes.present?
+ QA::Runtime::Logger.info(msg)
- QA::Support::Waiter.wait_until(sleep_interval: 1) do
- is_enabled = enabled?(key)
- is_enabled == enable
- end
+ Support::Retrier.retry_on_exception(sleep_interval: 2) do
+ set_feature(key, enable, scopes)
+
+ is_enabled = nil
+
+ QA::Support::Waiter.wait_until(sleep_interval: 1) do
+ is_enabled = enabled?(key, scopes)
+ is_enabled == enable || !enable && scopes.present?
+ end
+
+ if is_enabled == enable
+ QA::Runtime::Logger.info("Successfully #{enable ? 'en' : 'dis'}abled and verified feature flag: #{key}")
+ else
+ raise SetFeatureError, "#{key} was not #{enable ? 'en' : 'dis'}abled!" if enable
- raise SetFeatureError, "#{key} was not #{enable ? 'enabled' : 'disabled'}!" unless is_enabled == enable
+ QA::Runtime::Logger.warn("Feature flag scope was removed but the flag is still enabled globally.")
+ end
+ end
+ end
- QA::Runtime::Logger.info("Successfully #{enable ? 'enabled' : 'disabled'} and verified feature flag: #{key}")
+ def set_feature(key, value, **scopes)
+ scopes[:project] = scopes[:project].full_path if scopes.key?(:project)
+ scopes[:group] = scopes[:group].full_path if scopes.key?(:group)
+ scopes[:user] = scopes[:user].username if scopes.key?(:user)
+ request = Runtime::API::Request.new(api_client, "/features/#{key}")
+ response = post(request.url, scopes.merge({ value: value }))
+ unless response.code == QA::Support::Api::HTTP_STATUS_CREATED
+ raise SetFeatureError, "Setting feature flag #{key} to #{value} failed with `#{response}`."
+ end
end
- end
- def set_feature(key, value)
- request = Runtime::API::Request.new(api_client, "/features/#{key}")
- response = post(request.url, { value: value })
- unless response.code == QA::Support::Api::HTTP_STATUS_CREATED
- raise SetFeatureError, "Setting feature flag #{key} to #{value} failed with `#{response}`."
+ def scopes_to_s(**scopes)
+ key = scopes.each_key.first
+ s = "#{key}: "
+ case key
+ when :project, :group
+ s += scopes[key].full_path
+ when :user
+ s += scopes[key].username
+ when :feature_group
+ s += scopes[key]
+ else
+ raise UnknownScopeError, "Unknown scope: #{key}"
+ end
+
+ s
end
end
end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
index 29f131ac322..c3cb503ed3f 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
+++ b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
@@ -17,12 +17,12 @@ module QA
end
before do
- Runtime::Feature.enable_and_verify('gitaly_distributed_reads')
+ Runtime::Feature.enable(:gitaly_distributed_reads)
praefect_manager.wait_for_replication(project.id)
end
after do
- Runtime::Feature.disable_and_verify('gitaly_distributed_reads')
+ Runtime::Feature.disable(:gitaly_distributed_reads)
end
it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/979' do
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb
new file mode 100644
index 00000000000..82a06780830
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Merge request push options' do
+ # If run locally on GDK, push options need to be enabled on the host with the following command:
+ #
+ # git config --global receive.advertisepushoptions true
+
+ branch = "push-options-test-#{SecureRandom.hex(8)}"
+ title = "MR push options test #{SecureRandom.hex(8)}"
+ commit_message = 'Add README.md'
+
+ project = Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
+
+ it 'sets labels', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1032' do
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.commit_message = commit_message
+ push.branch_name = branch
+ push.merge_request_push_options = {
+ create: true,
+ title: title,
+ label: %w[one two three]
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+ expect(merge_request[:labels]).to include('one').and include('two').and include('three')
+ end
+
+ context 'when labels are set already' do
+ it 'removes them', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1033' do
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.file_content = "Unlabel test #{SecureRandom.hex(8)}"
+ push.commit_message = commit_message
+ push.branch_name = branch
+ push.new_branch = false
+ push.merge_request_push_options = {
+ title: title,
+ unlabel: %w[one three]
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+
+ aggregate_failures do
+ expect(merge_request[:labels]).to include('two')
+ expect(merge_request[:labels]).not_to include('one')
+ expect(merge_request[:labels]).not_to include('three')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb
new file mode 100644
index 00000000000..dde4708874d
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Merge request push options' do
+ # If run locally on GDK, push options need to be enabled on the host with the following command:
+ #
+ # git config --global receive.advertisepushoptions true
+
+ let(:branch) { "push-options-test-#{SecureRandom.hex(8)}" }
+ let(:title) { "MR push options test #{SecureRandom.hex(8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = "runner-for-#{project.name}"
+ runner.tags = ["runner-for-#{project.name}"]
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'sets merge when pipeline succeeds', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1037' do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ no-op:
+ tags:
+ - "runner-for-#{project.name}"
+ script: sleep 999 # Leave the pipeline pending
+ YAML
+ }
+ ]
+ )
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = branch
+ push.merge_request_push_options = {
+ create: true,
+ merge_when_pipeline_succeeds: true,
+ title: title
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+
+ merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
+ mr.project = project
+ mr.id = merge_request[:iid]
+ end
+
+ expect(merge_request.state).to eq('opened')
+ expect(merge_request.merge_status).to eq('checking')
+ expect(merge_request.merge_when_pipeline_succeeds).to be true
+ end
+
+ it 'merges when pipeline succeeds', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1036' do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ no-op:
+ tags:
+ - "runner-for-#{project.name}"
+ script: echo 'OK'
+ YAML
+ }
+ ]
+ )
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = branch
+ push.merge_request_push_options = {
+ create: true,
+ merge_when_pipeline_succeeds: true,
+ title: title
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+ expect(merge_request[:merge_when_pipeline_succeeds]).to be true
+
+ merge_request = Support::Waiter.wait_until(sleep_interval: 5) do
+ mr = Resource::MergeRequest.fabricate_via_api! do |mr|
+ mr.project = project
+ mr.id = merge_request[:iid]
+ end
+
+ next unless mr.state == 'merged'
+
+ mr
+ end
+
+ expect(merge_request.state).to eq('merged')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb
new file mode 100644
index 00000000000..d6bd668fa8a
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Merge request push options' do
+ # If run locally on GDK, push options need to be enabled on the host with the following command:
+ #
+ # git config --global receive.advertisepushoptions true
+
+ let(:branch) { "push-options-test-#{SecureRandom.hex(8)}" }
+ let(:title) { "MR push options test #{SecureRandom.hex(8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
+ end
+
+ it 'removes the source branch', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1035' do
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = branch
+ push.merge_request_push_options = {
+ create: true,
+ remove_source_branch: true,
+ title: title
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+
+ merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
+ mr.project = project
+ mr.id = merge_request[:iid]
+ end.merge_via_api!
+
+ expect(merge_request[:state]).to eq('merged')
+ expect(project).not_to have_branch(branch)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
new file mode 100644
index 00000000000..6072fd8c1a2
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Merge request push options' do
+ # If run locally on GDK, push options need to be enabled on the host with the following command:
+ #
+ # git config --global receive.advertisepushoptions true
+
+ let(:title) { "MR push options test #{SecureRandom.hex(8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
+ end
+
+ it 'sets a target branch', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1034' do
+ target_branch = "push-options-test-target-#{SecureRandom.hex(8)}"
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = target_branch
+ push.file_content = "Target branch test target branch #{SecureRandom.hex(8)}"
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = "push-options-test-#{SecureRandom.hex(8)}"
+ push.file_content = "Target branch test source branch #{SecureRandom.hex(8)}"
+ push.merge_request_push_options = {
+ create: true,
+ title: title,
+ target: target_branch
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+ expect(merge_request[:target_branch]).to eq(target_branch)
+
+ merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
+ mr.project = project
+ mr.id = merge_request[:iid]
+ end.merge_via_api!
+
+ expect(merge_request[:state]).to eq('merged')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb
new file mode 100644
index 00000000000..f49a8a229dc
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Merge request push options' do
+ # If run locally on GDK, push options need to be enabled on the host with the following command:
+ #
+ # git config --global receive.advertisepushoptions true
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-request-push-options'
+ project.initialize_with_readme = true
+ end
+ end
+
+ it 'sets title and description', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1038' do
+ description = "This is a test of MR push options"
+ title = "MR push options test #{SecureRandom.hex(8)}"
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.branch_name = "push-options-test-#{SecureRandom.hex(8)}"
+ push.merge_request_push_options = {
+ create: true,
+ title: title,
+ description: description
+ }
+ end
+
+ merge_request = project.merge_request_with_title(title)
+
+ expect(merge_request).not_to be_nil, "There was a problem creating the merge request"
+
+ aggregate_failures do
+ expect(merge_request[:title]).to eq(title)
+ expect(merge_request[:description]).to eq(description)
+ end
+
+ merge_request = Resource::MergeRequest.fabricate_via_api! do |mr|
+ mr.project = project
+ mr.id = merge_request[:iid]
+ end.merge_via_api!
+
+ expect(merge_request[:state]).to eq('merged')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
index af155b22618..f86bbee05c2 100644
--- a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
@@ -13,7 +13,7 @@ module QA
Runtime::ApplicationSettings.restore_application_settings(:default_branch_name)
end
- it 'sets the default branch name for a new project' do
+ it 'sets the default branch name for a new project', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1018' do
project = Resource::Project.fabricate_via_api! do |project|
project.name = "default-branch-name"
project.initialize_with_readme = true
@@ -32,7 +32,7 @@ module QA
end
end
- it 'allows a project to be created via the CLI with a different default branch name' do
+ it 'allows a project to be created via the CLI with a different default branch name', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1019' do
project_name = "default-branch-name-via-cli-#{SecureRandom.hex(8)}"
group = Resource::Group.fabricate_via_api!
diff --git a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
index 4bd99b4820e..548933d2cde 100644
--- a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
@@ -28,6 +28,13 @@ module QA
end
end
+ after do
+ # Delete the .netrc file created during this test so that subsequent tests don't try to use the logins
+ Git::Repository.perform do |repository|
+ repository.delete_netrc
+ end
+ end
+
it 'download archives of each user project then check they are different', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/427' do
archive_checksums = {}
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
index bd0d28c86be..e514507fcb6 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
@@ -18,7 +18,7 @@ module QA
QA::Resource::Group.fabricate_via_api! do |group|
group.sandbox = sandbox_group
group.api_client = owner_api_client
- group.name = 'group-with-2fa'
+ group.path = "group-with-2fa-#{SecureRandom.hex(8)}"
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb
index b011978dce6..c908b1c46a1 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan' do
+ RSpec.describe 'Plan', :reliable do
describe 'Milestones' do
include Support::Dates
diff --git a/qa/qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb
index 5f7e28190b2..564cfbb8399 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan' do
+ RSpec.describe 'Plan', :reliable do
describe 'Group milestone' do
include Support::Dates
diff --git a/qa/qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb
index 489691b4d0c..99d547acb26 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan' do
+ RSpec.describe 'Plan', :reliable do
describe 'Project milestone' do
include Support::Dates
diff --git a/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb b/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb
new file mode 100644
index 00000000000..7090427e5a4
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ context 'Design Management' do
+ let(:first_design) { Resource::Design.fabricate! }
+
+ let(:second_design) do
+ Resource::Design.fabricate! do |design|
+ design.issue = first_design.issue
+ design.filename = 'values.png'
+ end
+ end
+
+ let(:third_design) do
+ Resource::Design.fabricate! do |design|
+ design.issue = second_design.issue
+ design.filename = 'tanuki.jpg'
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ it 'user archives a design', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/274' do
+ third_design.issue.visit!
+
+ Page::Project::Issue::Show.perform do |issue|
+ issue.select_design(third_design.filename)
+
+ issue.archive_selected_designs
+
+ expect(issue).not_to have_design(third_design.filename)
+ expect(issue).to have_design(first_design.filename)
+ expect(issue).to have_design(second_design.filename)
+ end
+
+ Page::Project::Issue::Show.perform do |issue|
+ issue.select_design(second_design.filename)
+ issue.select_design(first_design.filename)
+
+ issue.archive_selected_designs
+
+ expect(issue).not_to have_design(first_design.filename)
+ expect(issue).not_to have_design(second_design.filename)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb
new file mode 100644
index 00000000000..7844d0d7ccb
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create', :requires_admin do
+ describe 'View merge request merge-ref diff' do
+ let(:merge_request) do
+ Resource::MergeRequest.fabricate_via_api! do |merge_request|
+ merge_request.title = 'This is a merge request'
+ merge_request.description = '... for viewing merge-ref and merge-base diffs'
+ merge_request.file_content = 'This exists on the source branch only'
+ end
+ end
+
+ let(:new_file_name) { "added_file-#{SecureRandom.hex(8)}.txt" }
+
+ before do
+ commit_to_branch(merge_request.target_branch, new_file_name)
+ commit_to_branch(merge_request.source_branch, new_file_name)
+
+ Flow::Login.sign_in
+ end
+
+ context 'when the feature flag default_merge_ref_for_diffs is enabled' do
+ before do
+ Runtime::Feature.enable('default_merge_ref_for_diffs', project: merge_request.project)
+
+ merge_request.visit!
+ end
+
+ it 'views the merge-ref diff by default' do
+ Page::MergeRequest::Show.perform do |mr_page|
+ mr_page.click_diffs_tab
+ mr_page.click_target_version_dropdown
+
+ expect(mr_page.version_dropdown_content).to include('master (HEAD)')
+ expect(mr_page.version_dropdown_content).not_to include('master (base)')
+ expect(mr_page).to have_file(merge_request.file_name)
+ expect(mr_page).not_to have_file(new_file_name)
+ end
+ end
+ end
+
+ context 'when the feature flag default_merge_ref_for_diffs is disabled' do
+ before do
+ Runtime::Feature.disable('default_merge_ref_for_diffs', project: merge_request.project)
+
+ merge_request.visit!
+ end
+
+ it 'views the merge-base diff by default' do
+ Page::MergeRequest::Show.perform do |mr_page|
+ mr_page.click_diffs_tab
+ mr_page.click_target_version_dropdown
+
+ expect(mr_page.version_dropdown_content).to include('master (HEAD)')
+ expect(mr_page.version_dropdown_content).to include('master (base)')
+ expect(mr_page).to have_file(merge_request.file_name)
+ expect(mr_page).to have_file(new_file_name)
+ end
+ end
+ end
+
+ def commit_to_branch(branch, file)
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = merge_request.project
+ commit.branch = branch
+ commit.commit_message = "Add new file on #{branch}"
+ commit.add_files(
+ [
+ {
+ file_path: file,
+ content: "This exists on source and target branches"
+ }
+ ]
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
index f96b424d233..83945a09587 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
@@ -3,8 +3,8 @@
module QA
RSpec.describe 'Create' do
describe 'Push mirror a repository over HTTP' do
- it 'configures and syncs LFS objects for a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/414' do
- Runtime::Feature.enable_and_verify('push_mirror_syncs_lfs')
+ it 'configures and syncs LFS objects for a (push) mirrored repository', :requires_admin, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/414' do
+ Runtime::Feature.enable(:push_mirror_syncs_lfs)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
index ef3b315506f..a85efe5829b 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb
@@ -36,6 +36,10 @@ module QA
Flow::Login.sign_in
end
+ after do
+ ssh_key.remove_via_api!
+ end
+
it 'clones, pushes, and pulls a snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/826' do
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = repository_uri_http
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
index 34f6b464f29..9789f7a1716 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb
@@ -36,6 +36,10 @@ module QA
Flow::Login.sign_in
end
+ after do
+ ssh_key.remove_via_api!
+ end
+
it 'clones, pushes, and pulls a project snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/833' do
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = repository_uri_http
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb
new file mode 100644
index 00000000000..376ce51273e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create', :requires_admin do
+ describe 'Multiple file snippet' do
+ before do
+ Runtime::Feature.enable('snippet_multiple_files')
+ end
+
+ after do
+ Runtime::Feature.disable('snippet_multiple_files')
+ end
+
+ it 'creates a personal snippet with multiple files', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/842' do
+ Flow::Login.sign_in
+
+ Page::Main::Menu.perform do |menu|
+ menu.go_to_more_dropdown_option(:snippets_link)
+ end
+
+ Resource::Snippet.fabricate_via_browser_ui! do |snippet|
+ snippet.title = 'Personal snippet with multiple files'
+ snippet.description = 'Snippet description'
+ snippet.visibility = 'Public'
+ snippet.file_name = 'First file name'
+ snippet.file_content = 'First file content'
+
+ snippet.add_files do |files|
+ files.append(name: 'Second file name', content: 'Second file content')
+ files.append(name: 'Third file name', content: 'Third file content')
+ end
+ end
+
+ Page::Dashboard::Snippet::Show.perform do |snippet|
+ expect(snippet).to have_snippet_title('Personal snippet with multiple files')
+ expect(snippet).to have_snippet_description('Snippet description')
+ expect(snippet).to have_visibility_type(/public/i)
+ expect(snippet).to have_file_name('First file name', 1)
+ expect(snippet).to have_file_content('First file content', 1)
+ expect(snippet).to have_file_name('Second file name', 2)
+ expect(snippet).to have_file_content('Second file content', 2)
+ expect(snippet).to have_file_name('Third file name', 3)
+ expect(snippet).to have_file_content('Third file content', 3)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
index 0a8f6e13b2e..d80fc4c5b95 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb
@@ -21,8 +21,8 @@ module QA
expect(snippet).to have_file_name('markdown_file.md')
expect(snippet).to have_file_content('Snippet heading')
expect(snippet).to have_file_content('Gitlab link')
- expect(snippet).not_to have_file_content('###')
- expect(snippet).not_to have_file_content('https://gitlab.com/')
+ expect(snippet).to have_no_file_content('###')
+ expect(snippet).to have_no_file_content('https://gitlab.com/')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb
index e8053600930..4f1d9ac1696 100644
--- a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_directory_management_spec.rb
@@ -4,7 +4,7 @@ module QA
RSpec.describe 'Create' do
context 'Wiki' do
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
- let(:new_path) { "a/new/path" }
+ let(:new_path) { "a/new/path-with-spaces" }
before do
Flow::Login.sign_in
@@ -23,7 +23,9 @@ module QA
Page::Project::Wiki::Edit.perform(&:click_save_changes)
Page::Project::Wiki::Show.perform do |wiki|
- expect(wiki).to have_directory(new_path)
+ expect(wiki).to have_directory('a')
+ expect(wiki).to have_directory('new')
+ expect(wiki).to have_directory('path with spaces')
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
index 326647b25f7..8de739f1559 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:max_wait) { 30 }
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb
new file mode 100644
index 00000000000..153ccafaa20
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify' do
+ describe 'Run pipeline', :requires_admin, :skip_live_env do
+ # [TODO]: Developer to remove :requires_admin and :skip_live_env once FF is removed in https://gitlab.com/gitlab-org/gitlab/-/issues/229632
+
+ context 'with web only rule' do
+ let(:feature_flag) { :new_pipeline_form }
+ let(:job_name) { 'test_job' }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'web-only-pipeline'
+ end
+ end
+
+ let!(:ci_file) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ #{job_name}:
+ tags:
+ - #{project.name}
+ script: echo 'OK'
+ only:
+ - web
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ before do
+ Runtime::Feature.enable(feature_flag) # [TODO]: Developer to remove when feature flag is removed
+ Flow::Login.sign_in
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ end
+
+ after do
+ Runtime::Feature.disable(feature_flag) # [TODO]: Developer to remove when feature flag is removed
+ end
+
+ it 'can trigger pipeline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/946' do
+ Page::Project::Pipeline::Index.perform do |index|
+ expect(index).not_to have_pipeline # should not auto trigger pipeline
+ index.click_run_pipeline_button
+ end
+
+ Page::Project::Pipeline::New.perform(&:click_run_pipeline_button)
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to have_job(job_name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
index a296d60b27c..9ce87f353d0 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Runner registration' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let!(:runner) do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
index f4edaaa84a8..5bfc88e45f2 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Code coverage statistics' do
let(:simplecov) { '\(\d+.\d+\%\) covered' }
let(:executor) { "qa-runner-#{Time.now.to_i}" }
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index a617f3b3e29..4ca356c9b65 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :docker, :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages do
describe 'Maven Repository' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index e97ede35610..43c708093b3 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :docker, :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages do
describe 'NPM registry' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index 18eb52830a2..abac4f2b91d 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -3,7 +3,7 @@
require 'digest/sha1'
module QA
- RSpec.describe 'Release', :docker, :runner do
+ RSpec.describe 'Release', :runner do
describe 'Git clone using a deploy key' do
before do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
index 47a1b3b5670..ece45d093a7 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Release', :docker, :runner, :reliable do
+ RSpec.describe 'Release', :runner, :reliable do
describe 'Parent-child pipelines dependent relationship' do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
index 9eb81244aa4..38cee0e62ca 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Release', :docker, :runner, :reliable do
+ RSpec.describe 'Release', :runner, :reliable do
describe 'Parent-child pipelines independent relationship' do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
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 53a1c8010af..6d31780f196 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
@@ -15,7 +15,7 @@ module QA
disable_optional_jobs(project)
end
- describe 'Auto DevOps support', :orchestrated, :kubernetes, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/230927', type: :stale } do
+ describe 'Auto DevOps support', :orchestrated, :kubernetes, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/251090', type: :stale } do
context 'when rbac is enabled' do
let(:cluster) { Service::KubernetesCluster.new.create! }
@@ -24,6 +24,8 @@ module QA
end
it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/702' do
+ skip('Test requires tunnel: see https://gitlab.com/gitlab-org/gitlab/-/issues/251090')
+
Flow::Login.sign_in
# Set an application secret CI variable (prefixed with K8S_SECRET_)
diff --git a/qa/qa/specs/features/sanity/framework_spec.rb b/qa/qa/specs/features/sanity/framework_spec.rb
index 611c6c7b1ff..feec56478c0 100644
--- a/qa/qa/specs/features/sanity/framework_spec.rb
+++ b/qa/qa/specs/features/sanity/framework_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- context 'Framework sanity checks', :orchestrated, :framework do
+ RSpec.describe 'Framework sanity checks', :orchestrated, :framework do
describe 'Passing orchestrated example' do
it 'succeeds' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index 08faacb6db3..5fc36b68e5c 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -40,7 +40,7 @@ module QA
return_response_or_raise(e)
end
- def put(url, payload)
+ def put(url, payload = nil)
RestClient::Request.execute(
method: :put,
url: url,
diff --git a/qa/qa/support/wait_for_requests.rb b/qa/qa/support/wait_for_requests.rb
index 943d7d510df..ebc473a7d86 100644
--- a/qa/qa/support/wait_for_requests.rb
+++ b/qa/qa/support/wait_for_requests.rb
@@ -9,18 +9,14 @@ module QA
def wait_for_requests(skip_finished_loading_check: false)
Waiter.wait_until(log: false) do
- finished_all_ajax_requests? && finished_all_axios_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true)
+ finished_all_ajax_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true)
end
- end
-
- def finished_all_axios_requests?
- Capybara.page.evaluate_script('window.pendingRequests || 0').zero? # rubocop:disable Style/NumericPredicate
+ rescue Repeater::WaitExceededError
+ raise $!, 'Page did not fully load. This could be due to an unending async request or loading icon.'
end
def finished_all_ajax_requests?
- return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"')
-
- Capybara.page.evaluate_script('jQuery.active').zero? # rubocop:disable Style/NumericPredicate
+ Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate
end
def finished_loading?(wait: DEFAULT_MAX_WAIT_TIME)