summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
commit7e9c479f7de77702622631cff2628a9c8dcbc627 (patch)
treec8f718a08e110ad7e1894510980d2155a6549197 /qa
parente852b0ae16db4052c1c567d9efa4facc81146e88 (diff)
downloadgitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'qa')
-rw-r--r--qa/Dockerfile4
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock2
-rwxr-xr-xqa/bin/qa16
-rw-r--r--qa/qa.rb2
-rw-r--r--qa/qa/flow/pipeline.rb17
-rw-r--r--qa/qa/page/base.rb15
-rw-r--r--qa/qa/page/component/design_management.rb2
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb10
-rw-r--r--qa/qa/page/component/note.rb87
-rw-r--r--qa/qa/page/component/select2.rb10
-rw-r--r--qa/qa/page/component/snippet.rb15
-rw-r--r--qa/qa/page/dashboard/snippet/edit.rb40
-rw-r--r--qa/qa/page/file/shared/commit_message.rb8
-rw-r--r--qa/qa/page/file/show.rb21
-rw-r--r--qa/qa/page/group/members.rb19
-rw-r--r--qa/qa/page/group/show.rb37
-rw-r--r--qa/qa/page/layout/flash.rb25
-rw-r--r--qa/qa/page/main/login.rb4
-rw-r--r--qa/qa/page/main/sign_up.rb16
-rw-r--r--qa/qa/page/merge_request/show.rb10
-rw-r--r--qa/qa/page/profile/personal_access_tokens.rb5
-rw-r--r--qa/qa/page/project/import/github.rb4
-rw-r--r--qa/qa/page/project/issue/index.rb8
-rw-r--r--qa/qa/page/project/issue/show.rb73
-rw-r--r--qa/qa/page/project/pipeline/show.rb10
-rw-r--r--qa/qa/page/project/settings/advanced.rb3
-rw-r--r--qa/qa/page/project/settings/mirroring_repositories.rb2
-rw-r--r--qa/qa/page/project/show.rb12
-rw-r--r--qa/qa/resource/file.rb43
-rw-r--r--qa/qa/resource/group.rb2
-rw-r--r--qa/qa/resource/issue.rb15
-rw-r--r--qa/qa/resource/project_imported_from_github.rb1
-rw-r--r--qa/qa/resource/project_snippet.rb8
-rw-r--r--qa/qa/resource/runner.rb2
-rw-r--r--qa/qa/resource/snippet.rb32
-rw-r--r--qa/qa/resource/user.rb2
-rw-r--r--qa/qa/runtime/application_settings.rb2
-rw-r--r--qa/qa/runtime/env.rb7
-rw-r--r--qa/qa/service/docker_run/gitlab_runner.rb2
-rw-r--r--qa/qa/service/praefect_manager.rb8
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb12
-rw-r--r--qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb12
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb2
-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/1_manage/login/login_via_instance_wide_saml_sso_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb39
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb85
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb53
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_merge_ref_diff_spec.rb28
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb57
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb31
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb32
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb36
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb54
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb60
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb138
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb82
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb111
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_via_web_only_spec.rb33
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb121
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb88
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb126
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb92
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb113
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb10
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb6
-rw-r--r--qa/qa/support/page/logging.rb2
-rw-r--r--qa/qa/tools/delete_subgroups.rb9
-rw-r--r--qa/spec/service/docker_run/gitlab_runner_spec.rb2
-rw-r--r--qa/spec/service/docker_run/k3s_spec.rb2
-rw-r--r--qa/spec/spec_helper.rb3
-rw-r--r--qa/spec/support/matchers/have_file.rb15
-rw-r--r--qa/spec/support/matchers/have_text.rb48
-rw-r--r--qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb2
-rw-r--r--qa/spec/support/shared_examples/scenario_shared_examples.rb106
94 files changed, 1875 insertions, 438 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 6310e4b290d..925c9758450 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.6-stretch
+FROM ruby:2.7-buster
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive"
@@ -65,7 +65,7 @@ COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/
COPY ./lib/gitlab.rb /home/gitlab/lib/
COPY ./lib/gitlab/utils.rb /home/gitlab/lib/gitlab/
COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
-RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --quiet
+RUN cd /home/gitlab/qa/ && bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
diff --git a/qa/Gemfile b/qa/Gemfile
index f00f26a5482..fa8fd40d5bb 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -23,5 +23,4 @@ gem 'rspec-parameterized', '~> 0.4.2'
group :development do
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem "ruby-debug-ide", "~> 0.7.0"
- gem "debase", "~> 0.2.4.1"
end
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 6cdedc3834d..883c5480689 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -175,4 +175,4 @@ DEPENDENCIES
timecop (~> 0.9.1)
BUNDLED WITH
- 1.17.3
+ 2.1.4
diff --git a/qa/bin/qa b/qa/bin/qa
index 6a772e93cee..cbaad4bd7ad 100755
--- a/qa/bin/qa
+++ b/qa/bin/qa
@@ -2,6 +2,20 @@
require_relative '../qa'
+scenario = ARGV.shift
+
+if scenario.to_s.empty?
+ puts <<~INFO
+ For instructions on how to run tests, please see https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/README.md
+
+ If you are using gitlab-qa, please see https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md
+
+ If you see this message after starting a docker container and you intended to launch an omnibus-gitlab instance, try removing `-qa` from the image name.
+ INFO
+
+ exit
+end
+
QA::Scenario
- .const_get(ARGV.shift)
+ .const_get(scenario)
.launch!(ARGV)
diff --git a/qa/qa.rb b/qa/qa.rb
index f281a4b6ef4..ee7f97e64b1 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -19,6 +19,7 @@ module QA
autoload :Saml, 'qa/flow/saml'
autoload :User, 'qa/flow/user'
autoload :MergeRequest, 'qa/flow/merge_request'
+ autoload :Pipeline, 'qa/flow/pipeline'
end
##
@@ -402,6 +403,7 @@ module QA
module Layout
autoload :Banner, 'qa/page/layout/banner'
+ autoload :Flash, 'qa/page/layout/flash'
autoload :PerformanceBar, 'qa/page/layout/performance_bar'
end
diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb
new file mode 100644
index 00000000000..ff23907c081
--- /dev/null
+++ b/qa/qa/flow/pipeline.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module Pipeline
+ module_function
+
+ # In some cases we don't need to wait for anything, blocked, running or pending is acceptable
+ # Some cases only need pipeline to finish with different condition (completion, success or replication)
+ def visit_latest_pipeline(pipeline_condition: nil)
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:"wait_for_latest_pipeline_#{pipeline_condition}") if pipeline_condition
+ Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index abd9332ced0..ce4eac7fbc4 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -34,19 +34,26 @@ module QA
@retry_later_backoff = QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME
end
+ def inspect
+ # For prettier failure messages
+ # Eg.: "expected QA::Page::File::Show not to have file "QA Test - File name"
+ # Instead of "expected #<QA::Page::File::Show:0x000055c6511e07b8 @retry_later_backoff=60> not to have file "QA Test - File name"
+ self.class.to_s
+ end
+
def assert_no_element(name)
assert_no_selector(element_selector_css(name))
end
- def refresh
+ def refresh(skip_finished_loading_check: false)
page.refresh
- wait_for_requests
+ wait_for_requests(skip_finished_loading_check: skip_finished_loading_check)
end
- def wait_until(max_duration: 60, sleep_interval: 0.1, reload: true, raise_on_failure: true)
+ def wait_until(max_duration: 60, sleep_interval: 0.1, reload: true, raise_on_failure: true, skip_finished_loading_check_on_refresh: false)
Support::Waiter.wait_until(max_duration: max_duration, sleep_interval: sleep_interval, raise_on_failure: raise_on_failure) do
- yield || (reload && refresh && false)
+ yield || (reload && refresh(skip_finished_loading_check: skip_finished_loading_check_on_refresh) && false)
end
end
diff --git a/qa/qa/page/component/design_management.rb b/qa/qa/page/component/design_management.rb
index fafbda58b07..cccf1cf1a58 100644
--- a/qa/qa/page/component/design_management.rb
+++ b/qa/qa/page/component/design_management.rb
@@ -64,7 +64,7 @@ module QA
# It doesn't work as a CSS selector.
# So instead we use the name attribute as a locator
within_element(:design_dropzone_content) do
- page.attach_file("design_file", design_file_path, make_visible: { display: 'block' })
+ page.attach_file("upload_file", design_file_path, make_visible: { display: 'block' })
end
filename = ::File.basename(design_file_path)
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index 82347ee209a..cc39260ce38 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -64,8 +64,14 @@ module QA
end
def has_assignee?(username)
- page.within(element_selector_css(:assignee_block)) do
- has_text?(username)
+ within_element(:assignee_block) do
+ has_text?(username, wait: 120)
+ end
+ end
+
+ def has_no_assignee_named?(username)
+ within_element(:assignee_block) do
+ has_no_text?(username, wait: 120)
end
end
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index e6defd2ec0c..5ac72d73c78 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -13,20 +13,35 @@ module QA
element :toggle_comments_button
end
+ base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
+ element :comment_button
+ element :comment_field
+ element :discussion_menu_item
+ element :note_dropdown
+ 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_menu_item
+ base.view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
+ element :discussion_filter_dropdown, required: true
+ element :filter_menu_item
+ end
+
+ base.view 'app/assets/javascripts/notes/components/discussion_filter_note.vue' do
+ element :discussion_filter_container
end
base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
element :discussion_content
end
+ base.view 'app/assets/javascripts/notes/components/noteable_note.vue' do
+ element :noteable_note_container
+ end
+
base.view 'app/assets/javascripts/notes/components/note_actions.vue' do
element :note_edit_button
end
@@ -36,6 +51,10 @@ module QA
element :reply_comment_button
end
+ base.view 'app/assets/javascripts/notes/components/note_header.vue' do
+ element :system_note_content
+ end
+
base.view 'app/assets/javascripts/notes/components/toggle_replies_widget.vue' do
element :expand_replies_button
element :collapse_replies_button
@@ -44,12 +63,30 @@ module QA
base.view 'app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue' do
element :skeleton_note_placeholder
end
+
+ base.view 'app/views/shared/notes/_form.html.haml' do
+ element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern
+ element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
+ end
end
def collapse_replies
click_element :collapse_replies_button
end
+ # Attachment option should be an absolute path
+ def comment(text, attachment: nil, filter: :all_activities)
+ method("select_#{filter}_filter").call
+ fill_element :comment_field, "#{text}\n"
+
+ unless attachment.nil?
+ QA::Page::Component::Dropzone.new(self, '.new-note')
+ .attach_file(attachment)
+ end
+
+ click_element :comment_button
+ end
+
def edit_comment(text)
click_element :note_edit_button
fill_element :reply_field, text
@@ -60,6 +97,18 @@ module QA
click_element :expand_replies_button
end
+ def has_comment?(comment_text)
+ has_element?(:noteable_note_container, text: comment_text, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
+ end
+
+ def has_system_note?(note_text)
+ has_element?(:system_note_content, text: note_text, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
+ end
+
+ def noteable_note_item
+ find_element(:noteable_note_container)
+ end
+
def reply_to_discussion(position, reply_text)
type_reply_to_discussion(position, reply_text)
click_element :reply_comment_button
@@ -71,6 +120,26 @@ module QA
end
end
+ def select_all_activities_filter
+ select_filter_with_text('Show all activity')
+ end
+
+ def select_comments_only_filter
+ select_filter_with_text('Show comments only')
+
+ wait_until do
+ has_no_element?(:system_note_content)
+ end
+ end
+
+ def select_history_only_filter
+ select_filter_with_text('Show history only')
+
+ wait_until do
+ has_element?(:discussion_filter_container) && has_no_element?(:noteable_note_container)
+ end
+ end
+
def start_discussion(text)
fill_element :comment_field, text
click_element :note_dropdown
@@ -90,6 +159,18 @@ module QA
def wait_for_loading
has_no_element?(:skeleton_note_placeholer)
end
+
+ private
+
+ def select_filter_with_text(text)
+ retry_on_exception do
+ click_element(:title)
+ click_element :discussion_filter_dropdown
+ find_element(:filter_menu_item, text: text).click
+
+ wait_for_loading
+ end
+ end
end
end
end
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
index 87aed0105aa..7a835af2575 100644
--- a/qa/qa/page/component/select2.rb
+++ b/qa/qa/page/component/select2.rb
@@ -38,6 +38,16 @@ module QA
select_item(item_text)
end
+ def search_and_select_exact(item_text)
+ QA::Runtime::Logger.info "Searching and selecting: #{item_text}"
+
+ search_item(item_text)
+
+ raise QA::Page::Base::ElementNotFound, %Q(Couldn't find option named "#{item_text}") unless has_item?(item_text)
+
+ find('.select2-result-label', text: item_text, exact_text: true).click
+ end
+
def expand_select_list
find('span.select2-arrow').click
end
diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb
index 9a4b06d8ac7..459c02ec883 100644
--- a/qa/qa/page/component/snippet.rb
+++ b/qa/qa/page/component/snippet.rb
@@ -110,6 +110,18 @@ module QA
end
end
+ def has_no_file_name?(file_name, file_number = nil)
+ if file_number
+ within_element_by_index(:file_title_content, file_number - 1) do
+ has_no_text?(file_name)
+ end
+ else
+ within_element(:file_title_content) do
+ has_no_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
@@ -170,6 +182,7 @@ module QA
def add_comment(comment)
fill_element(:note_field, comment)
click_element(:comment_button)
+ wait_until(reload: false) { has_element?(:note_author_content) }
end
def has_comment_author?(author_username)
@@ -194,6 +207,7 @@ module QA
click_element(:edit_comment_button)
fill_element(:edit_note_field, comment)
click_element(:save_comment_button)
+ wait_until(reload: false) { has_element?(:note_author_content) }
end
def delete_comment(comment)
@@ -201,6 +215,7 @@ module QA
accept_alert do
click_element(:delete_comment_button)
end
+ wait_until(reload: false) { has_no_text?(comment) }
end
end
end
diff --git a/qa/qa/page/dashboard/snippet/edit.rb b/qa/qa/page/dashboard/snippet/edit.rb
index 37c0747aea4..40add146e97 100644
--- a/qa/qa/page/dashboard/snippet/edit.rb
+++ b/qa/qa/page/dashboard/snippet/edit.rb
@@ -9,6 +9,15 @@ module QA
element :submit_button, required: true
end
+ view 'app/assets/javascripts/snippets/components/snippet_blob_edit.vue' do
+ element :file_name_field
+ element :file_holder_container
+ end
+
+ view 'app/assets/javascripts/blob/components/blob_edit_header.vue' do
+ element :delete_file_button
+ end
+
def add_to_file_content(content)
text_area.set content
text_area.has_text?(content) # wait for changes to take effect
@@ -18,7 +27,38 @@ module QA
choose(visibility_type)
end
+ def click_add_file
+ click_element(:add_file_button)
+ end
+
+ 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 click_delete_file(file_number)
+ within_element_by_index(:file_holder_container, file_number - 1) do
+ click_element(:delete_file_button)
+ end
+ end
+
def save_changes
+ wait_until(reload: false) { !find_element(:submit_button).disabled? }
click_element(:submit_button, Page::Dashboard::Snippet::Show)
end
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
index 823ce7bf7f9..906f5f3581a 100644
--- a/qa/qa/page/file/shared/commit_message.rb
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -13,11 +13,19 @@ module QA
base.view 'app/views/shared/_commit_message_container.html.haml' do
element :commit_message, "text_area_tag 'commit_message'" # rubocop:disable QA/ElementWithPattern
end
+
+ base.view 'app/views/projects/commits/_commit.html.haml' do
+ element :commit_content
+ end
end
def add_commit_message(message)
fill_in 'commit_message', with: message
end
+
+ def has_commit_message?(text)
+ has_element?(:commit_content, text: text)
+ end
end
end
end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
index f5f44909f25..665b3c34dcc 100644
--- a/qa/qa/page/file/show.rb
+++ b/qa/qa/page/file/show.rb
@@ -7,16 +7,25 @@ module QA
include Shared::CommitMessage
include Project::SubMenus::Settings
include Project::SubMenus::Common
+ include Layout::Flash
view 'app/helpers/blob_helper.rb' do
element :edit_button, "_('Edit')" # rubocop:disable QA/ElementWithPattern
element :delete_button, '_("Delete")' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/projects/blob/_header_content.html.haml' do
+ element :file_name_content
+ end
+
view 'app/views/projects/blob/_remove.html.haml' do
element :delete_file_button, "button_tag 'Delete file'" # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/shared/_file_highlight.html.haml' do
+ element :file_content
+ end
+
def click_edit
click_on 'Edit'
end
@@ -28,6 +37,18 @@ module QA
def click_delete_file
click_on 'Delete file'
end
+
+ def has_file?(name)
+ has_element?(:file_name_content, text: name)
+ end
+
+ def has_no_file?(name)
+ has_no_element?(:file_name_content, text: name)
+ end
+
+ def has_file_content?(text)
+ has_element?(:file_content, text: text)
+ end
end
end
end
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index dce18ee5c55..16e447a2be5 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -16,17 +16,24 @@ module QA
element :invite_member_button
end
- view 'app/views/shared/members/_member.html.haml' do
+ view 'app/assets/javascripts/pages/groups/group_members/index.js' do
element :member_row
+ element :groups_list
+ element :group_row
+ end
+
+ view 'app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue' do
element :access_level_dropdown
+ element :access_level_link
+ end
+
+ view 'app/assets/javascripts/vue_shared/components/members/action_buttons/remove_member_button.vue' do
element :delete_member_button
- element :developer_access_level_link, 'qa_selector: "#{role.downcase}_access_level_link"' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
end
view 'app/views/groups/group_members/index.html.haml' do
element :invite_group_tab
element :groups_list_tab
- element :groups_list
end
view 'app/views/shared/members/_invite_group.html.haml' do
@@ -34,10 +41,6 @@ module QA
element :invite_group_button
end
- view 'app/views/shared/members/_group.html.haml' do
- element :group_row
- end
-
def select_group(group_name)
click_element :group_select_field
search_and_select(group_name)
@@ -57,7 +60,7 @@ module QA
def update_access_level(username, access_level)
within_element(:member_row, text: username) do
click_element :access_level_dropdown
- click_element "#{access_level.downcase}_access_level_link"
+ click_element :access_level_link, text: access_level
end
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 7639def98b7..38d919be4db 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -7,11 +7,8 @@ module QA
include Page::Component::GroupsFilter
view 'app/views/groups/_home_panel.html.haml' do
- element :new_project_or_subgroup_dropdown
- element :new_project_or_subgroup_dropdown_toggle
- element :new_project_option
- element :new_subgroup_option
- element :new_in_group_button
+ element :new_project_button
+ element :new_subgroup_button
end
view 'app/assets/javascripts/groups/constants.js' do
@@ -26,8 +23,9 @@ module QA
click_link name
end
- def has_new_project_or_subgroup_dropdown?
- has_element?(:new_project_or_subgroup_dropdown)
+ def has_new_project_and_new_subgroup_buttons?
+ has_element?(:new_project_button)
+ has_element?(:new_subgroup_button)
end
def has_subgroup?(name)
@@ -35,15 +33,11 @@ module QA
end
def go_to_new_subgroup
- select_kind :new_subgroup_option
-
- click_element :new_in_group_button
+ click_element :new_subgroup_button
end
def go_to_new_project
- select_kind :new_project_option
-
- click_element :new_in_group_button
+ click_element :new_project_button
end
def leave_group
@@ -51,23 +45,6 @@ module QA
click_element :leave_group_link
end
end
-
- private
-
- def select_kind(kind)
- QA::Support::Retrier.retry_on_exception(sleep_interval: 1.0) do
- within_element(:new_project_or_subgroup_dropdown) do
- # May need to click again because it is possible to click the button quicker than the JS is bound
- wait_until(reload: false) do
- click_element :new_project_or_subgroup_dropdown_toggle
-
- has_element?(kind)
- end
-
- click_element kind
- end
- end
- end
end
end
end
diff --git a/qa/qa/page/layout/flash.rb b/qa/qa/page/layout/flash.rb
new file mode 100644
index 00000000000..6bd3d6ab76b
--- /dev/null
+++ b/qa/qa/page/layout/flash.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Layout
+ module Flash
+ extend QA::Page::PageConcern
+
+ def self.included(base)
+ super
+
+ base.view 'app/views/layouts/_flash.html.haml' do
+ element :flash_container
+ end
+ end
+
+ def has_notice?(message)
+ within_element(:flash_container) do
+ has_text?(message)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 8eb28eb53e7..265e2b7573c 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -125,9 +125,9 @@ module QA
click_element :sign_in_tab
end
- def switch_to_register_tab
+ def switch_to_register_page
set_initial_password_if_present
- click_element :register_tab
+ click_element :register_link
end
def switch_to_ldap_tab
diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
index 98bbbc53027..f8e85798012 100644
--- a/qa/qa/page/main/sign_up.rb
+++ b/qa/qa/page/main/sign_up.rb
@@ -13,20 +13,18 @@ module QA
element :new_user_register_button
end
- view 'app/views/registrations/welcome.html.haml' do
+ view 'app/views/registrations/welcome/show.html.haml' do
element :get_started_button
end
def sign_up!(user)
- fill_element :new_user_first_name_field, user.first_name
- fill_element :new_user_last_name_field, user.last_name
- fill_element :new_user_username_field, user.username
- fill_element :new_user_email_field, user.email
- fill_element :new_user_password_field, user.password
-
- signed_in = retry_until do
+ signed_in = retry_until(raise_on_failure: false) do
+ fill_element :new_user_first_name_field, user.first_name
+ fill_element :new_user_last_name_field, user.last_name
+ fill_element :new_user_username_field, user.username
+ fill_element :new_user_email_field, user.email
+ fill_element :new_user_password_field, user.password
click_element :new_user_register_button if has_element?(:new_user_register_button)
-
click_element :get_started_button if has_element?(:get_started_button)
Page::Main::Menu.perform(&:has_personal_area?)
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 164f25389c0..ac4d38e5918 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -23,7 +23,6 @@ module QA
element :merge_button
element :fast_forward_message, 'Fast-forward merge without a merge commit' # rubocop:disable QA/ElementWithPattern
element :merge_moment_dropdown
- element :merge_when_pipeline_succeeds_option
element :merge_immediately_option
end
@@ -178,6 +177,10 @@ module QA
has_element?(:file_name_content, text: file_name)
end
+ def has_no_file?(file_name)
+ has_no_element?(:file_name_content, text: file_name)
+ end
+
def has_merge_button?
refresh
@@ -219,6 +222,11 @@ module QA
raise "Merge did not appear to be successful" unless merged?
end
+ def merge_immediately!
+ click_element(:merge_moment_dropdown)
+ click_element(:merge_immediately_option)
+ end
+
def merged?
has_element?(:merged_status_content, text: 'The changes were merged into', wait: 60)
end
diff --git a/qa/qa/page/profile/personal_access_tokens.rb b/qa/qa/page/profile/personal_access_tokens.rb
index fd191fa3e27..caa8c0ceb40 100644
--- a/qa/qa/page/profile/personal_access_tokens.rb
+++ b/qa/qa/page/profile/personal_access_tokens.rb
@@ -6,8 +6,11 @@ module QA
module Page
module Profile
class PersonalAccessTokens < Page::Base
- view 'app/views/shared/access_tokens/_form.html.haml' do
+ view 'app/assets/javascripts/access_tokens/components/expires_at_field.vue' do
element :expiry_date_field
+ end
+
+ view 'app/views/shared/access_tokens/_form.html.haml' do
element :access_token_name_field
element :create_token_button
end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index b533e0096a8..ad1746258ea 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -67,7 +67,9 @@ module QA
end
def wait_for_success
- wait_until(max_duration: 60, sleep_interval: 1.0, reload: false) do
+ # TODO: set reload:false and remove skip_finished_loading_check_on_refresh when
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/231542 is fixed
+ wait_until(max_duration: 60, sleep_interval: 5.0, reload: true, skip_finished_loading_check_on_refresh: true) do
page.has_content?('Done', wait: 1.0)
end
end
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index 0b47bec2cf1..64bd62c2b54 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -15,13 +15,13 @@ module QA
element :avatar_counter_content
end
- view 'app/views/projects/issues/export_csv/_button.html.haml' do
+ view 'app/views/shared/issuable/csv_export/_button.html.haml' do
element :export_as_csv_button
end
- view 'app/views/projects/issues/export_csv/_modal.html.haml' do
+ view 'app/views/shared/issuable/csv_export/_modal.html.haml' do
element :export_issues_button
- element :export_issues_modal
+ element :export_issuable_modal
end
view 'app/views/projects/issues/import_csv/_button.html.haml' do
@@ -64,7 +64,7 @@ module QA
end
def export_issues_modal
- find_element(:export_issues_modal)
+ find_element(:export_issuable_modal)
end
def go_to_jira_import_form
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index a02617def9e..f7bd74d1882 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -10,20 +10,6 @@ module QA
include Page::Component::DesignManagement
include Page::Component::Issuable::Sidebar
- view 'app/assets/javascripts/notes/components/comment_form.vue' do
- element :comment_button
- element :comment_field
- end
-
- view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
- element :discussion_filter, required: true
- element :filter_options
- end
-
- view 'app/assets/javascripts/notes/components/noteable_note.vue' do
- element :noteable_note_item
- end
-
view 'app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue' do
element :remove_related_issue_button
end
@@ -33,11 +19,6 @@ module QA
element :reopen_issue_button
end
- view 'app/views/shared/notes/_form.html.haml' do
- element :new_note_form, 'new-note' # rubocop:disable QA/ElementWithPattern
- element :new_note_form, 'attr: :note' # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/assets/javascripts/related_issues/components/add_issuable_form.vue' do
element :add_issue_button
end
@@ -51,8 +32,8 @@ module QA
end
view 'app/assets/javascripts/related_issues/components/related_issues_list.vue' do
- element :related_issuable_item
- element :related_issues_loading_icon
+ element :related_issuable_content
+ element :related_issues_loading_placeholder
end
def relate_issue(issue)
@@ -62,11 +43,11 @@ module QA
end
def related_issuable_item
- find_element(:related_issuable_item)
+ find_element(:related_issuable_content)
end
def wait_for_related_issues_to_load
- has_no_element?(:related_issues_loading_icon, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
+ has_no_element?(:related_issues_loading_placeholder, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
end
def click_remove_related_issue_button
@@ -80,55 +61,9 @@ module QA
click_element :close_issue_button
end
- # Adds a comment to an issue
- # attachment option should be an absolute path
- def comment(text, attachment: nil, filter: :all_activities)
- method("select_#{filter}_filter").call
- fill_element :comment_field, "#{text}\n"
-
- unless attachment.nil?
- QA::Page::Component::Dropzone.new(self, '.new-note')
- .attach_file(attachment)
- end
-
- click_element :comment_button
- end
-
- def has_comment?(comment_text)
- has_element?(:noteable_note_item, text: comment_text, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
- end
-
- def noteable_note_item
- find_element(:noteable_note_item)
- end
-
- def select_all_activities_filter
- select_filter_with_text('Show all activity')
- end
-
- def select_comments_only_filter
- select_filter_with_text('Show comments only')
- end
-
- def select_history_only_filter
- select_filter_with_text('Show history only')
- end
-
def has_metrics_unfurled?
has_element?(:prometheus_graph_widgets, wait: 30)
end
-
- private
-
- def select_filter_with_text(text)
- retry_on_exception do
- click_element(:title)
- click_element :discussion_filter
- find_element(:filter_options, text: text).click
-
- wait_for_loading
- end
- end
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 0fb5238a308..b32d099d2b0 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -26,6 +26,10 @@ module QA
element :child_pipeline
end
+ view 'app/assets/javascripts/reports/components/report_section.vue' do
+ element :expand_report_button
+ end
+
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
@@ -78,6 +82,12 @@ module QA
end
end
+ def expand_license_report
+ within_element(:license_report_widget) do
+ click_element(:expand_report_button)
+ end
+ end
+
def click_on_first_job
first('.js-pipeline-graph-job-link', wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).click
end
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
index 97519c3906c..757084fc5b9 100644
--- a/qa/qa/page/project/settings/advanced.rb
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -11,6 +11,9 @@ module QA
view 'app/views/projects/edit.html.haml' do
element :project_path_field
element :change_path_button
+ end
+
+ view 'app/views/projects/_transfer.html.haml' do
element :transfer_button
end
diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb
index ddace6d0533..ce369c90a9f 100644
--- a/qa/qa/page/project/settings/mirroring_repositories.rb
+++ b/qa/qa/page/project/settings/mirroring_repositories.rb
@@ -98,7 +98,7 @@ module QA
sleep 5
refresh
- wait_until(sleep_interval: 1) do
+ wait_until(max_duration: 180, sleep_interval: 1) do
within_element_by_index(:mirrored_repository_row, row_index) do
last_update = find_element(:mirror_last_update_at_cell, wait: 0)
last_update.has_text?('just now') || last_update.has_text?('seconds')
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index d81be2803bd..4f0cf55c127 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -4,9 +4,11 @@ module QA
module Page
module Project
class Show < Page::Base
+ include Layout::Flash
include Page::Component::ClonePanel
include Page::Component::Breadcrumbs
include Page::Project::SubMenus::Settings
+ include Page::File::Shared::CommitMessage
view 'app/assets/javascripts/repository/components/preview/index.vue' do
element :blob_viewer_content
@@ -121,6 +123,12 @@ module QA
end
end
+ def has_no_file?(name)
+ within_element(:file_tree_table) do
+ has_no_element?(:file_name_link, text: name)
+ end
+ end
+
def has_name?(name)
has_element?(:project_name_content, text: name)
end
@@ -129,10 +137,6 @@ module QA
has_element?(:blob_viewer_content, text: text)
end
- def last_commit_content
- find_element(:commit_content).text
- end
-
def new_merge_request
wait_until(reload: true) do
has_css?(element_selector_css(:create_merge_request))
diff --git a/qa/qa/resource/file.rb b/qa/qa/resource/file.rb
index f573f3e89f0..0d2bf9890ea 100644
--- a/qa/qa/resource/file.rb
+++ b/qa/qa/resource/file.rb
@@ -5,14 +5,21 @@ module QA
class File < Base
attr_accessor :author_email,
:author_name,
- :branch,
:content,
:commit_message,
:name
+ attr_writer :branch
attribute :project do
Project.fabricate! do |resource|
resource.name = 'project-with-new-file'
+
+ # Creating the first file via the Wed IDE is tested in
+ # browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb
+ # So here we want to use the old blob viewer, which is not
+ # available via the UI unless at least one file exists, which
+ # is why we create the project with a readme file.
+ resource.initialize_with_readme = true
end
end
@@ -22,28 +29,23 @@ module QA
@commit_message = 'QA Test - Commit message'
end
+ def branch
+ @branch ||= "master"
+ end
+
def fabricate!
project.visit!
- Page::Project::Show.perform(&:create_first_new_file!)
-
- Page::Project::WebIDE::Edit.perform do |ide|
- ide.add_file(@name, @content)
- ide.commit_changes(@commit_message)
- ide.go_to_project
- end
+ Page::Project::Show.perform(&:create_new_file!)
- Page::Project::Show.perform do |project|
- project.click_file(@name)
+ Page::File::Form.perform do |form|
+ form.add_name(@name)
+ form.add_content(@content)
+ form.add_commit_message(@commit_message)
+ form.commit_changes
end
end
- def resource_web_url(resource)
- super
- rescue ResourceURLMissingError
- # this particular resource does not expose a web_url property
- end
-
def api_get_path
"/projects/#{CGI.escape(project.path_with_namespace)}/repository/files/#{CGI.escape(@name)}"
end
@@ -54,13 +56,20 @@ module QA
def api_post_body
{
- branch: @branch || "master",
+ branch: branch,
author_email: @author_email || Runtime::User.default_email,
author_name: @author_name || Runtime::User.username,
content: content,
commit_message: commit_message
}
end
+
+ private
+
+ def transform_api_resource(api_resource)
+ api_resource[:web_url] = "#{Runtime::Scenario.gitlab_address}/#{project.full_path}/-/tree/#{branch}/#{api_resource[:file_path]}"
+ api_resource
+ end
end
end
end
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 1cb33a7c71c..2e29ec9a6a7 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -44,7 +44,7 @@ module QA
# Ensure that the group was actually created
group_show.wait_until(sleep_interval: 1) do
group_show.has_text?(path) &&
- group_show.has_new_project_or_subgroup_dropdown?
+ group_show.has_new_project_and_new_subgroup_buttons?
end
end
end
diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb
index d96d8d744d2..a6bd8987077 100644
--- a/qa/qa/resource/issue.rb
+++ b/qa/qa/resource/issue.rb
@@ -57,6 +57,21 @@ module QA
hash[:weight] = @weight if @weight
end
end
+
+ def api_put_path
+ "/projects/#{project.id}/issues/#{iid}"
+ end
+
+ def set_issue_assignees(assignee_ids:)
+ put_body = { assignee_ids: assignee_ids }
+ response = put Runtime::API::Request.new(api_client, api_put_path).url, put_body
+
+ unless response.code == HTTP_STATUS_OK
+ raise ResourceUpdateFailedError, "Could not update issue assignees to #{assignee_ids}. Request returned (#{response.code}): `#{response}`."
+ end
+
+ QA::Runtime::Logger.debug("Successfully updated issue assignees to #{assignee_ids}")
+ end
end
end
end
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
index df28d63b113..0b817b345fd 100644
--- a/qa/qa/resource/project_imported_from_github.rb
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -6,6 +6,7 @@ module QA
module Resource
class ProjectImportedFromGithub < Resource::Project
def fabricate!
+ self.import = true
super
group.visit!
diff --git a/qa/qa/resource/project_snippet.rb b/qa/qa/resource/project_snippet.rb
index 6fa38baaa91..c262499664e 100644
--- a/qa/qa/resource/project_snippet.rb
+++ b/qa/qa/resource/project_snippet.rb
@@ -31,6 +31,14 @@ module QA
new_snippet.click_create_snippet_button
end
end
+
+ def api_get_path
+ "/projects/#{project.id}/snippets/#{snippet_id}"
+ end
+
+ def api_post_path
+ "/projects/#{project.id}/snippets"
+ end
end
end
end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index b2a36f92ffe..2a0823d648e 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -29,7 +29,7 @@ module QA
end
def executor_image
- @executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6'
+ @executor_image || 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
end
def fabricate_via_api!
diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb
index c4ea6447209..6fdcb1cd29b 100644
--- a/qa/qa/resource/snippet.rb
+++ b/qa/qa/resource/snippet.rb
@@ -3,7 +3,7 @@
module QA
module Resource
class Snippet < Base
- attr_accessor :title, :description, :file_content, :visibility, :file_name
+ attr_accessor :title, :description, :file_content, :visibility, :file_name, :snippet_id
def initialize
@title = 'New snippet title'
@@ -36,6 +36,36 @@ module QA
new_page.click_create_snippet_button
end
end
+
+ def fabricate_via_api!
+ resource_web_url(api_post)
+ rescue ResourceNotFoundError
+ super
+ end
+
+ def api_get_path
+ "/snippets/#{snippet_id}"
+ end
+
+ def api_post_path
+ '/snippets'
+ end
+
+ def api_post_body
+ {
+ title: title,
+ description: description,
+ visibility: visibility.downcase,
+ files: all_file_contents
+ }
+ end
+
+ def all_file_contents
+ @files.insert(0, { name: @file_name, content: @file_content })
+ @files.each do |file|
+ file[:file_path] = file.delete(:name)
+ end
+ end
end
end
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 5cd4147e154..ca30ff12480 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -75,7 +75,7 @@ module QA
end
else
Page::Main::Login.perform do |login|
- login.switch_to_register_tab
+ login.switch_to_register_page
end
Page::Main::SignUp.perform do |signup|
signup.sign_up!(self)
diff --git a/qa/qa/runtime/application_settings.rb b/qa/qa/runtime/application_settings.rb
index c78f4258721..428ed20c83f 100644
--- a/qa/qa/runtime/application_settings.rb
+++ b/qa/qa/runtime/application_settings.rb
@@ -33,7 +33,7 @@ module QA
def api_client
@api_client ||= Runtime::API::Client.as_admin
- rescue AuthorizationError => e
+ rescue API::Client::AuthorizationError => e
raise "Administrator access is required to set application settings. #{e.message}"
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index ddaf35a2d65..4c4dd416093 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -8,7 +8,7 @@ module QA
module Env
extend self
- attr_writer :personal_access_token, :ldap_username, :ldap_password
+ attr_writer :personal_access_token
ENV_VARIABLES = Gitlab::QA::Runtime::Env::ENV_VARIABLES
@@ -293,6 +293,11 @@ module QA
@ldap_username ||= ENV['GITLAB_LDAP_USERNAME']
end
+ def ldap_username=(ldap_username)
+ @ldap_username = ldap_username # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ ENV['GITLAB_LDAP_USERNAME'] = ldap_username
+ end
+
def ldap_password
@ldap_password ||= ENV['GITLAB_LDAP_PASSWORD']
end
diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb
index e15047a0f1d..a5b129eb1f9 100644
--- a/qa/qa/service/docker_run/gitlab_runner.rb
+++ b/qa/qa/service/docker_run/gitlab_runner.rb
@@ -21,7 +21,7 @@ module QA
@name = name || "qa-runner-#{SecureRandom.hex(4)}"
@run_untagged = true
@executor = :shell
- @executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6'
+ @executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7'
super()
end
diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb
index 3a17acbb317..ab4f28c292f 100644
--- a/qa/qa/service/praefect_manager.rb
+++ b/qa/qa/service/praefect_manager.rb
@@ -206,14 +206,14 @@ module QA
def wait_for_new_primary_node(node)
QA::Runtime::Logger.info("Wait until #{node} is the primary node")
- with_praefect_log do |log|
+ with_praefect_log(max_duration: 120) do |log|
break true if log['msg'] == 'primary node changed' && log['newPrimary'] == node
end
end
def wait_for_new_primary
QA::Runtime::Logger.info("Wait until a new primary node is selected")
- with_praefect_log do |log|
+ with_praefect_log(max_duration: 120) do |log|
break true if log['msg'] == 'primary node changed'
end
end
@@ -406,8 +406,8 @@ module QA
end
end
- def with_praefect_log
- wait_until_shell_command("docker exec #{@praefect} bash -c 'tail -n 1 /var/log/gitlab/praefect/current'") do |line|
+ 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
diff --git a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
index 90f58090ccd..223ed02bb47 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
+++ b/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- context 'Gitaly automatic failover and manual recovery', :orchestrated, :gitaly_cluster, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238953', type: :flaky } do
+ context 'Gitaly automatic failover and recovery', :orchestrated, :gitaly_cluster, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238953', type: :flaky } do
# Variables shared between contexts. They're used and shared between
# contexts so they can't be `let` variables.
praefect_manager = Service::PraefectManager.new
@@ -66,17 +66,13 @@ module QA
end
context 'when recovering from dataloss after failover' do
- it 'allows reconciliation', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238187', type: :stale }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/977' do
+ it 'automatically reconciles', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238187', type: :stale }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/977' do
# Start the old primary node again
praefect_manager.start_primary_node
praefect_manager.wait_for_health_check_current_primary_node
- # Confirm dataloss (i.e., inconsistent nodes)
- expect(praefect_manager.replicated?(project.id)).to be false
-
- # Reconcile nodes to recover from dataloss
- praefect_manager.reconcile_nodes
- praefect_manager.wait_for_replication(project.id)
+ # Confirm automatic reconciliation
+ expect(praefect_manager.replicated?(project.id)).to be true
# Confirm that all commits are available after reconciliation
expect(project.commits.map { |commit| commit[:message].chomp })
diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
index 6072fd8c1a2..e02d32bc4c7 100644
--- a/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
+++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb
@@ -25,6 +25,18 @@ module QA
push.file_content = "Target branch test target branch #{SecureRandom.hex(8)}"
end
+ # Confirm the target branch can be checked out to avoid a race condition
+ # where the subsequent push option attempts to create an MR before the target branch is ready.
+ Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.clone
+ repository.configure_identity('GitLab QA', 'root@gitlab.com')
+ repository.checkout(target_branch)
+ end
+ end
+
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.branch_name = "push-options-test-#{SecureRandom.hex(8)}"
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
index 23de213012e..163469e1e88 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_recovery_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- context 'Manage', :requires_admin, :skip_live_env do
+ RSpec.describe 'Manage', :requires_admin, :skip_live_env do
describe '2FA' do
let(:owner_user) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_2fa_owner_username_1, Runtime::Env.gitlab_qa_2fa_owner_password_1)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
index e81ebd5fa9d..7f3c3049499 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/2fa_ssh_recovery_spec.rb
@@ -22,7 +22,7 @@ module QA
it 'allows 2FA code recovery via ssh' do
recovery_code = Support::SSH.perform do |ssh|
ssh.key = ssh_key
- ssh.uri = address.gsub(uri.port.to_s, ssh_port)
+ ssh.uri = address.gsub(/(?<=:)(#{uri.port})/, ssh_port)
ssh.setup
output = ssh.reset_2fa_codes
output.scan(/([A-Za-z0-9]{16})\n/).flatten.first
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 e514507fcb6..12a1b419f8b 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
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- context 'Manage', :requires_admin, :skip_live_env do
+ RSpec.describe 'Manage', :requires_admin, :skip_live_env do
describe '2FA' do
let(:owner_user) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_2fa_owner_username_1, Runtime::Env.gitlab_qa_2fa_owner_password_1)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
index f6aecff9f26..e4ac59cf5e0 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -12,7 +12,9 @@ module QA
login_page.login('user1', 'user1pass')
end
- expect(page).to have_content('Welcome to GitLab')
+ Page::Dashboard::Welcome.perform do |welcome|
+ expect(welcome).to have_content('Welcome to GitLab')
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 11a6bf6fdfa..2bb03b6154f 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -13,11 +13,39 @@ module QA
end
end
- RSpec.describe 'Manage', :skip_signup_disabled do
+ RSpec.describe 'Manage', :skip_signup_disabled, :requires_admin do
+ describe 'while LDAP is enabled', :orchestrated, :ldap_no_tls, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/935' do
+ before do
+ # When LDAP is enabled, a previous test might have created a token for the LDAP 'tanuki' user who is not an admin
+ # So we need to set it to nil in order to create a new token for admin user so that we are able to set_application_settings
+ # Also, when GITLAB_LDAP_USERNAME is provided, it is used to create a token. This also needs to be set to nil temporarily
+ # for the same reason as above.
+
+ @personal_access_token = Runtime::Env.personal_access_token
+ Runtime::Env.personal_access_token = nil
+ ldap_username = Runtime::Env.ldap_username
+ Runtime::Env.ldap_username = nil
+
+ disable_require_admin_approval_after_user_signup
+
+ Runtime::Env.ldap_username = ldap_username
+ end
+
+ it_behaves_like 'registration and login'
+
+ after do
+ Runtime::Env.personal_access_token = @personal_access_token
+ end
+ end
+
describe 'standard', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/936' do
+ before(:all) do
+ disable_require_admin_approval_after_user_signup
+ end
+
it_behaves_like 'registration and login'
- context 'when user account is deleted', :requires_admin do
+ context 'when user account is deleted' do
let(:user) do
Resource::User.fabricate_via_api! do |resource|
resource.api_client = admin_api_client
@@ -61,11 +89,10 @@ module QA
end
end
end
- end
- RSpec.describe 'Manage', :orchestrated, :ldap_no_tls, :skip_signup_disabled do
- describe 'while LDAP is enabled', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/935' do
- it_behaves_like 'registration and login'
+ def disable_require_admin_approval_after_user_signup
+ Runtime::ApplicationSettings.set_application_settings(require_admin_approval_after_user_signup: false)
+ sleep 10 # It takes a moment for the setting to come into effect
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
index 27350176a1e..e71cbeb9837 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -15,9 +15,9 @@ module QA
Page::Project::Menu.perform(&:click_members)
Page::Project::Members.perform do |members|
members.add_member(user.username)
- end
- expect(page).to have_content(/@#{user.username}(\n| )?Given access/)
+ expect(members).to have_content(/@#{user.username}( Is using seat)?(\n| )?Given access/)
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
index 6d07f72a044..2f2f40cba3b 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
@@ -11,12 +11,14 @@ module QA
project.description = 'create awesome project test'
end
- expect(page).to have_content(created_project.name)
- expect(page).to have_content(
- /Project \S?awesome-project\S+ was successfully created/
- )
- expect(page).to have_content('create awesome project test')
- expect(page).to have_content('The repository for this project is empty')
+ Page::Project::Show.perform do |project|
+ expect(project).to have_content(created_project.name)
+ expect(project).to have_content(
+ /Project \S?awesome-project\S+ was successfully created/
+ )
+ expect(project).to have_content('create awesome project test')
+ expect(project).to have_content('The repository for this project is empty')
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
index 83dfb2d9639..d54ce0ac0fc 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
@@ -1,30 +1,35 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :github, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/26952', type: :bug } do
- describe 'Project import from GitHub' do
+ RSpec.describe 'Manage', :github, :requires_admin do
+ describe 'Project import' do
+ let!(:user) do
+ Resource::User.fabricate_via_api! do |resource|
+ resource.api_client = Runtime::API::Client.as_admin
+ end
+ end
+
+ let(:group) { Resource::Group.fabricate_via_api! }
+
let(:imported_project) do
- Resource::ProjectImportedFromGithub.fabricate! do |project|
+ Resource::ProjectImportedFromGithub.fabricate_via_browser_ui! do |project|
project.name = 'imported-project'
- project.personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = 'gitlab-qa/test-project'
+ project.group = group
+ project.github_personal_access_token = Runtime::Env.github_access_token
+ project.github_repository_path = 'gitlab-qa-github/test-project'
end
end
- after do
- # We need to delete the imported project because it's impossible to import
- # the same GitHub project twice for a given user.
- api_client = Runtime::API::Client.new(:gitlab)
- delete_project_request = Runtime::API::Request.new(api_client, "/projects/#{CGI.escape("#{Runtime::Namespace.path}/#{imported_project.name}")}")
- delete delete_project_request.url
-
- expect_status(202)
+ before do
+ group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ end
- Page::Main::Menu.perform(&:sign_out_if_signed_in)
+ after do
+ user.remove_via_api!
end
- it 'user imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/385' do
- Flow::Login.sign_in
+ it 'imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/385' do
+ Flow::Login.sign_in(as: user)
imported_project # import the project
@@ -44,25 +49,28 @@ module QA
end
def verify_repository_import
- expect(page).to have_content('This test project is used for automated GitHub import by GitLab QA.')
- expect(page).to have_content(imported_project.name)
+ Page::Project::Show.perform do |project|
+ expect(project).to have_content('This test project is used for automated GitHub import by GitLab QA.')
+ expect(project).to have_content(imported_project.name)
+ end
end
def verify_issues_import
QA::Support::Retrier.retry_on_exception do
Page::Project::Menu.perform(&:click_issues)
- expect(page).to have_content('This is a sample issue')
- click_link 'This is a sample issue'
+ Page::Project::Issue::Show.perform do |issue_page|
+ expect(issue_page).to have_content('This is a sample issue')
- expect(page).to have_content('We should populate this project with issues, pull requests and wiki pages.')
+ click_link 'This is a sample issue'
- # Comments
- comment_text = 'This is a comment from @rymai.'
+ expect(issue_page).to have_content('This is a sample first comment')
+
+ # Comments
+ comment_text = 'This is a comment from @sliaquat'
- Page::Project::Issue::Show.perform do |issue_page|
expect(issue_page).to have_comment(comment_text)
- expect(issue_page).to have_label('enhancement')
+ expect(issue_page).to have_label('custom new label')
expect(issue_page).to have_label('help wanted')
expect(issue_page).to have_label('good first issue')
end
@@ -71,26 +79,23 @@ module QA
def verify_merge_requests_import
Page::Project::Menu.perform(&:click_merge_requests)
- expect(page).to have_content('Improve README.md')
- click_link 'Improve README.md'
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_content('Improve readme')
- expect(page).to have_content('This improves the README file a bit.')
+ click_link 'Improve readme'
- # Review comment are not supported yet
- expect(page).not_to have_content('Really nice change.')
+ expect(merge_request).to have_content('This improves the README file a bit.')
- # Comments
- expect(page).to have_content('Nice work! This is a comment from @rymai.')
+ # Comments
+ expect(merge_request).to have_content('[PR comment by @sliaquat] Nice work!')
- # Diff comments
- expect(page).to have_content('[Review comment] I like that!')
- expect(page).to have_content('[Review comment] Nice blank line.')
- expect(page).to have_content('[Single diff comment] Much better without this line!')
+ # Diff comments
+ expect(merge_request).to have_content('[Single diff comment] Good riddance')
+ expect(merge_request).to have_content('[Single diff comment] Nice addition')
- Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_label('bug')
- expect(merge_request).to have_label('enhancement')
+ expect(merge_request).to have_label('documentation')
end
end
@@ -107,7 +112,9 @@ module QA
def verify_wiki_import
Page::Project::Menu.perform(&:click_wiki)
- expect(page).to have_content('Welcome to the test-project wiki!')
+ Page::Project::Wiki::Show.perform do |wiki|
+ expect(wiki).to have_content('Welcome to the test-project wiki!')
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
index fd6d26153ea..3609d083fde 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -13,9 +13,11 @@ module QA
end.project.visit!
Page::Project::Menu.perform(&:click_activity)
- Page::Project::Activity.perform(&:click_push_events)
+ Page::Project::Activity.perform do |activity|
+ activity.click_push_events
- expect(page).to have_content('pushed new branch master')
+ expect(activity).to have_content('pushed new branch master')
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
index 6b309716f55..22157d648ca 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
@@ -26,7 +26,7 @@ module QA
expect(show).not_to have_content(my_first_reply)
show.expand_replies
- expect(show).to have_content(my_first_reply)
+ expect(show).to have_comment(my_first_reply)
expect(show).not_to have_content(one_reply)
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
index c9ae47459c4..d3780186f36 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
@@ -16,11 +16,11 @@ module QA
show.comment(first_version_of_comment)
- expect(show).to have_content(first_version_of_comment)
+ expect(show).to have_comment(first_version_of_comment)
show.edit_comment(second_version_of_comment)
- expect(show).to have_content(second_version_of_comment)
+ expect(show).to have_comment(second_version_of_comment)
expect(show).not_to have_content(first_version_of_comment)
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 863c394a9f9..9550572bd5c 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -19,7 +19,7 @@ module QA
end
end
- it 'closes an issue', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225303', type: :bug }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/852' do
+ it 'closes an issue', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/225303', type: :bug }, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1085' do
closed_issue.visit!
Page::Project::Issue::Show.perform do |issue_page|
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
index 9a054e7d1c8..e275c3decd3 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', :reliable do
+ RSpec.describe 'Plan' do
describe 'filter issue comments activities' do
before do
Flow::Login.sign_in
@@ -18,16 +18,16 @@ module QA
show.comment(my_own_comment, filter: :comments_only)
expect(show).not_to have_content(made_the_issue_confidential)
- expect(show).to have_content(my_own_comment)
+ expect(show).to have_comment(my_own_comment)
show.select_all_activities_filter
- expect(show).to have_content(made_the_issue_confidential)
- expect(show).to have_content(my_own_comment)
+ expect(show).to have_system_note(made_the_issue_confidential)
+ expect(show).to have_comment(my_own_comment)
show.select_history_only_filter
- expect(show).to have_content(made_the_issue_confidential)
+ expect(show).to have_system_note(made_the_issue_confidential)
expect(show).not_to have_content(my_own_comment)
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb
new file mode 100644
index 00000000000..26a83fc3caa
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Plan', :requires_admin, :actioncable, :orchestrated do
+ describe 'Assignees' do
+ let(:user1) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
+ let(:user2) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-to-test-assignees'
+ end
+ end
+
+ before do
+ Runtime::Feature.enable('real_time_issue_sidebar', project: project)
+ Runtime::Feature.enable('broadcast_issue_updates', project: project)
+
+ Flow::Login.sign_in
+
+ project.add_member(user1)
+ project.add_member(user2)
+ end
+
+ after do
+ Runtime::Feature.disable('real_time_issue_sidebar', project: project)
+ Runtime::Feature.disable('broadcast_issue_updates', project: project)
+ end
+
+ it 'update without refresh', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1048' do
+ issue = Resource::Issue.fabricate_via_api! do |issue|
+ issue.project = project
+ issue.assignee_ids = [user1.id]
+ end
+
+ issue.visit!
+
+ Page::Project::Issue::Show.perform do |show|
+ expect(show).to have_assignee(user1.name)
+
+ issue.set_issue_assignees(assignee_ids: [user2.id])
+
+ expect(show).to have_assignee(user2.name)
+ expect(show).to have_no_assignee_named(user1.name)
+
+ issue.set_issue_assignees(assignee_ids: [])
+
+ expect(show).to have_no_assignee_named(user1.name)
+ expect(show).to have_no_assignee_named(user2.name)
+ 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
index 7844d0d7ccb..970615e8b90 100644
--- 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
@@ -3,8 +3,15 @@
module QA
RSpec.describe 'Create', :requires_admin do
describe 'View merge request merge-ref diff' do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'merge-ref-diff'
+ end
+ end
+
let(:merge_request) do
Resource::MergeRequest.fabricate_via_api! do |merge_request|
+ merge_request.project = project
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'
@@ -13,16 +20,14 @@ module QA
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)
+ Runtime::Feature.enable('default_merge_ref_for_diffs', project: project)
+
+ commit_to_branch(merge_request.target_branch, new_file_name)
+ commit_to_branch(merge_request.source_branch, new_file_name)
+
+ Flow::Login.sign_in
merge_request.visit!
end
@@ -42,7 +47,12 @@ module QA
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)
+ Runtime::Feature.disable('default_merge_ref_for_diffs', project: project)
+
+ commit_to_branch(merge_request.target_branch, new_file_name)
+ commit_to_branch(merge_request.source_branch, new_file_name)
+
+ Flow::Login.sign_in
merge_request.visit!
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
deleted file mode 100644
index 5aa5f0fc0a3..00000000000
--- a/qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- describe 'Files management' do
- it 'user creates, edits and deletes a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/451' do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
-
- # Create
- file_name = 'QA Test - File name'
- file_content = 'QA Test - File content'
- commit_message_for_create = 'QA Test - Create new file'
-
- Resource::File.fabricate_via_browser_ui! do |file|
- file.name = file_name
- file.content = file_content
- file.commit_message = commit_message_for_create
- end
-
- expect(page).to have_content(file_name)
- expect(page).to have_content(file_content)
- expect(page).to have_content(commit_message_for_create)
-
- # Edit
- updated_file_content = 'QA Test - Updated file content'
- commit_message_for_update = 'QA Test - Update file'
-
- Page::File::Show.perform(&:click_edit)
-
- Page::File::Form.act do
- remove_content
- add_content(updated_file_content)
- add_commit_message(commit_message_for_update)
- commit_changes
- end
-
- expect(page).to have_content('Your changes have been successfully committed.')
- expect(page).to have_content(updated_file_content)
- expect(page).to have_content(commit_message_for_update)
-
- # Delete
- commit_message_for_delete = 'QA Test - Delete file'
-
- Page::File::Show.act do
- click_delete
- add_commit_message(commit_message_for_delete)
- click_delete_file
- end
-
- expect(page).to have_content('The file has been successfully deleted.')
- expect(page).to have_content(commit_message_for_delete)
- expect(page).to have_no_content(file_name)
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
new file mode 100644
index 00000000000..cd333b3cea2
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ context 'File management' do
+ file_name = 'QA Test - File name'
+ file_content = 'QA Test - File content'
+ commit_message_for_create = 'QA Test - Create new file'
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ it 'user creates a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1093' do
+ Resource::File.fabricate_via_browser_ui! do |file|
+ file.name = file_name
+ file.content = file_content
+ file.commit_message = commit_message_for_create
+ end
+
+ Page::File::Show.perform do |file|
+ aggregate_failures 'file details' do
+ expect(file).to have_file(file_name)
+ expect(file).to have_file_content(file_content)
+ expect(file).to have_commit_message(commit_message_for_create)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
new file mode 100644
index 00000000000..903001aa4f0
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ context 'File management' do
+ let(:file) { Resource::File.fabricate_via_api! }
+
+ commit_message_for_delete = 'QA Test - Delete file'
+
+ before do
+ Flow::Login.sign_in
+ file.visit!
+ end
+
+ it 'user deletes a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1095' do
+ Page::File::Show.perform do |file|
+ file.click_delete
+ file.add_commit_message(commit_message_for_delete)
+ file.click_delete_file
+ end
+
+ Page::Project::Show.perform do |project|
+ aggregate_failures 'file details' do
+ expect(project).to have_notice('The file has been successfully deleted.')
+ expect(project).to have_commit_message(commit_message_for_delete)
+ expect(project).not_to have_file(file.name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
new file mode 100644
index 00000000000..0da774b557f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ context 'File management' do
+ let(:file) { Resource::File.fabricate_via_api! }
+
+ updated_file_content = 'QA Test - Updated file content'
+ commit_message_for_update = 'QA Test - Update file'
+
+ before do
+ Flow::Login.sign_in
+ file.visit!
+ end
+
+ it 'user edits a file via the Web', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1094' do
+ Page::File::Show.perform(&:click_edit)
+
+ Page::File::Form.perform do |file|
+ file.remove_content
+ file.add_content(updated_file_content)
+ file.add_commit_message(commit_message_for_update)
+ file.commit_changes
+ end
+
+ Page::File::Show.perform do |file|
+ aggregate_failures 'file details' do
+ expect(file).to have_notice('Your changes have been successfully committed.')
+ expect(file).to have_file_content(updated_file_content)
+ expect(file).to have_commit_message(commit_message_for_update)
+ end
+ 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 45afa252305..e4a492d3487 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,7 +3,7 @@
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
+ it 'configures and syncs LFS objects for a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1075' do
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/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index 00d4acbd1e1..f01a3b21eee 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -35,8 +35,11 @@ module QA
# Check that the target project has the commit from the source
target_project.visit!
- expect(page).to have_content('README.md')
- expect(page).to have_content('This is a test project')
+
+ Page::Project::Show.perform do |project|
+ expect(project).to have_content('README.md')
+ expect(project).to have_content('This is a test project')
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb
new file mode 100644
index 00000000000..469335db5ab
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Multiple file snippet' do
+ let(:personal_snippet) do
+ Resource::Snippet.fabricate_via_api! do |snippet|
+ snippet.title = 'Personal snippet to add file to'
+ snippet.file_name = 'Original file name'
+ snippet.file_content = 'Original file content'
+ end
+ end
+
+ let(:project_snippet) do
+ Resource::ProjectSnippet.fabricate_via_api! do |snippet|
+ snippet.title = 'Project snippet to add file to'
+ snippet.file_name = 'Original file name'
+ snippet.file_content = 'Original file content'
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ shared_examples 'adding file to snippet' do |snippet_type|
+ it "adds second file to an existing #{snippet_type} to make it multi-file" do
+ send(snippet_type).visit!
+
+ Page::Dashboard::Snippet::Show.perform(&:click_edit_button)
+
+ Page::Dashboard::Snippet::Edit.perform do |snippet|
+ snippet.click_add_file
+ snippet.fill_file_name('Second file name', 2)
+ snippet.fill_file_content('Second file content', 2)
+ snippet.save_changes
+ end
+
+ Page::Dashboard::Snippet::Show.perform do |snippet|
+ aggregate_failures 'file names and contents' do
+ expect(snippet).to have_file_name('Original file name', 1)
+ expect(snippet).to have_file_content('Original file content', 1)
+ expect(snippet).to have_file_name('Second file name', 2)
+ expect(snippet).to have_file_content('Second file content', 2)
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'adding file to snippet', :personal_snippet
+ it_behaves_like 'adding file to snippet', :project_snippet
+ end
+ end
+end
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 a3f6d521766..efd61a2e63a 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
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', quarantine: { only: { subdomain: :staging }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } do
+ RSpec.describe 'Create' do
describe 'Version control for personal snippets' do
let(:new_file) { 'new_snippet_file' }
let(:changed_content) { 'changes' }
@@ -22,13 +22,13 @@ module QA
end
let(:repository_uri_http) do
- snippet
+ snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:get_repository_uri_http)
end
let(:repository_uri_ssh) do
ssh_key
- snippet
+ snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:get_repository_uri_ssh)
end
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 be56b870490..79e2677da66 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
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', quarantine: { only: { subdomain: :staging }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } do
+ RSpec.describe 'Create' do
describe 'Version control for project snippets' do
let(:new_file) { 'new_snippet_file' }
let(:changed_content) { 'changes' }
@@ -22,13 +22,13 @@ module QA
end
let(:repository_uri_http) do
- snippet
+ snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:get_repository_uri_http)
end
let(:repository_uri_ssh) do
ssh_key
- snippet
+ snippet.visit!
Page::Dashboard::Snippet::Show.perform(&:get_repository_uri_ssh)
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb
new file mode 100644
index 00000000000..ca6ea5db65d
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ describe 'Multiple file snippet' do
+ let(:personal_snippet) do
+ Resource::Snippet.fabricate_via_api! do |snippet|
+ snippet.title = 'Personal snippet to delete file from'
+ snippet.file_name = 'Original file name'
+ snippet.file_content = 'Original file content'
+
+ snippet.add_files do |files|
+ files.append(name: 'Second file name', content: 'Second file content')
+ end
+ end
+ end
+
+ let(:project_snippet) do
+ Resource::ProjectSnippet.fabricate_via_api! do |snippet|
+ snippet.title = 'Project snippet to delete file from'
+ snippet.file_name = 'Original file name'
+ snippet.file_content = 'Original file content'
+
+ snippet.add_files do |files|
+ files.append(name: 'Second file name', content: 'Second file content')
+ end
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ shared_examples 'deleting file from snippet' do |snippet_type|
+ it "deletes second file from an existing #{snippet_type} to make it single-file" do
+ send(snippet_type).visit!
+
+ Page::Dashboard::Snippet::Show.perform(&:click_edit_button)
+
+ Page::Dashboard::Snippet::Edit.perform do |snippet|
+ snippet.click_delete_file(2)
+ snippet.save_changes
+ end
+
+ Page::Dashboard::Snippet::Show.perform do |snippet|
+ aggregate_failures 'file names and contents' do
+ expect(snippet).to have_file_name('Original file name')
+ expect(snippet).to have_file_content('Original file content')
+ expect(snippet).to have_no_file_name('Second file name')
+ expect(snippet).to have_no_file_content('Second file content')
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'deleting file from snippet', :personal_snippet
+ it_behaves_like 'deleting file from snippet', :project_snippet
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb
index 6b21d84cb13..971c5371d44 100644
--- a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb
@@ -18,7 +18,7 @@ module QA
context 'when the snippet is public' do
it 'can be shared with not signed-in users', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1016' do
- snippet
+ snippet.visit!
sharing_link = Page::Dashboard::Snippet::Show.perform do |snippet|
expect(snippet).to have_embed_dropdown
@@ -40,7 +40,7 @@ module QA
context 'when the snippet is changed to private' do
it 'does not display Embed/Share dropdown', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1015' do
- snippet
+ snippet.visit!
Page::Dashboard::Snippet::Show.perform do |snippet|
expect(snippet).to have_embed_dropdown
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 8de739f1559..1e6cb4047f9 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
@@ -65,8 +65,7 @@ module QA
)
end.project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ Flow::Pipeline.visit_latest_pipeline
{
'test-success': :passed,
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb
new file mode 100644
index 00000000000..cedc2db2a1a
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'faker'
+
+module QA
+ RSpec.describe 'Verify', :runner, :requires_admin, :skip_live_env do
+ describe "Include multiple files from a project" do
+ let(:feature_flag) { :ci_include_multiple_files_from_project }
+ let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
+ let(:expected_text) { Faker::Lorem.sentence }
+ let(:unexpected_text) { Faker::Lorem.sentence }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-pipeline-1'
+ end
+ end
+
+ let(:other_project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-pipeline-2'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ before do
+ Runtime::Feature.enable(feature_flag)
+ Flow::Login.sign_in
+ add_included_files
+ add_main_ci_file
+ project.visit!
+ view_the_last_pipeline
+ end
+
+ after do
+ Runtime::Feature.disable(feature_flag)
+ runner.remove_via_api!
+ end
+
+ it 'runs the pipeline with composed config', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1082' do
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ aggregate_failures 'pipeline has all expected jobs' do
+ expect(pipeline).to have_job('build')
+ expect(pipeline).to have_job('test')
+ expect(pipeline).to have_job('deploy')
+ end
+
+ pipeline.click_job('test')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ aggregate_failures 'main CI is not overridden' do
+ expect(job.output).to have_no_content("#{unexpected_text}")
+ expect(job.output).to have_content("#{expected_text}")
+ end
+ end
+ end
+
+ private
+
+ def add_main_ci_file
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add config file'
+ commit.add_files([main_ci_file])
+ end
+ end
+
+ def add_included_files
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = other_project
+ commit.commit_message = 'Add files'
+ commit.add_files([included_file_1, included_file_2])
+ end
+ end
+
+ def view_the_last_pipeline
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_success)
+ Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ end
+
+ def main_ci_file
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ include:
+ - project: #{other_project.full_path}
+ file:
+ - file1.yml
+ - file2.yml
+
+ build:
+ stage: build
+ tags: ["#{executor}"]
+ script: echo 'build'
+
+ test:
+ stage: test
+ tags: ["#{executor}"]
+ script: echo "#{expected_text}"
+ YAML
+ }
+ end
+
+ def included_file_1
+ {
+ file_path: 'file1.yml',
+ content: <<~YAML
+ test:
+ stage: test
+ tags: ["#{executor}"]
+ script: echo "#{unexpected_text}"
+ YAML
+ }
+ end
+
+ def included_file_2
+ {
+ file_path: 'file2.yml',
+ content: <<~YAML
+ deploy:
+ stage: deploy
+ tags: ["#{executor}"]
+ script: echo 'deploy'
+ YAML
+ }
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb
new file mode 100644
index 00000000000..c5d73d2fd7d
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'faker'
+
+module QA
+ RSpec.describe 'Verify', :runner do
+ context 'When pipeline is blocked' do
+ let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-blocked-pipeline'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ let!(:ci_file) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ test_blocked_pipeline:
+ stage: build
+ tags: [#{executor}]
+ script: echo 'OK!'
+
+ manual_job:
+ stage: test
+ needs: [test_blocked_pipeline]
+ script: echo do not click me
+ when: manual
+
+ dummy_job:
+ stage: deploy
+ needs: [manual_job]
+ script: echo nothing
+ YAML
+ ]
+ )
+ end
+ end
+
+ let(:merge_request) do
+ Resource::MergeRequest.fabricate_via_api! do |merge_request|
+ merge_request.project = project
+ merge_request.description = Faker::Lorem.sentence
+ merge_request.target_new_branch = false
+ merge_request.file_name = 'custom_file.txt'
+ merge_request.file_content = Faker::Lorem.sentence
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ merge_request.visit!
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'can still merge MR successfully', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/971' do
+ Page::MergeRequest::Show.perform do |show|
+ show.wait_until(reload: false) { show.has_pipeline_status?('running') }
+ show.merge_immediately!
+
+ expect(show).to be_merged
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb
new file mode 100644
index 00000000000..eafe28c1ee6
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require 'faker'
+
+module QA
+ RSpec.describe 'Verify', :runner, :requires_admin do
+ describe "Pass dotenv variables to downstream via bridge" do
+ let(:feature_flag) { :ci_bridge_dependency_variables }
+ let(:executor_1) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
+ let(:executor_2) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
+
+ let(:upstream_project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-pipeline-1'
+ end
+ end
+
+ let(:downstream_project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-pipeline-2'
+ end
+ end
+
+ let!(:runner_1) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = upstream_project
+ runner.name = executor_1
+ runner.tags = [executor_1]
+ end
+ end
+
+ let!(:runner_2) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = downstream_project
+ runner.name = executor_2
+ runner.tags = [executor_2]
+ end
+ end
+
+ before do
+ Runtime::Feature.enable(feature_flag)
+ Flow::Login.sign_in
+ add_ci_file(downstream_project, downstream_ci_file)
+ add_ci_file(upstream_project, upstream_ci_file)
+ upstream_project.visit!
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success')
+ end
+
+ after do
+ Runtime::Feature.disable(feature_flag)
+ runner_1.remove_via_api!
+ runner_2.remove_via_api!
+ end
+
+ it 'runs the pipeline with composed config', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1086' do
+ Page::Project::Pipeline::Show.perform do |parent_pipeline|
+ Support::Waiter.wait_until { parent_pipeline.has_child_pipeline? }
+ parent_pipeline.expand_child_pipeline
+ parent_pipeline.click_job('downstream_test')
+ end
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show).to have_passed(timeout: 360)
+ end
+ end
+
+ private
+
+ def add_ci_file(project, file)
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add config file'
+ commit.add_files([file])
+ end
+ end
+
+ def upstream_ci_file
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ build:
+ stage: build
+ tags: ["#{executor_1}"]
+ script: echo "MY_VAR=hello" >> variables.env
+ artifacts:
+ reports:
+ dotenv: variables.env
+
+ trigger:
+ stage: deploy
+ variables:
+ PASSED_MY_VAR: $MY_VAR
+ trigger: #{downstream_project.full_path}
+ YAML
+ }
+ end
+
+ def downstream_ci_file
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ downstream_test:
+ stage: test
+ tags: ["#{executor_2}"]
+ script: '[ "$PASSED_MY_VAR" = hello ]; exit "$?"'
+ YAML
+ }
+ end
+ end
+ end
+end
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
index 153ccafaa20..b79bda108af 100644
--- 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
@@ -2,11 +2,8 @@
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
-
+ describe 'Run pipeline', only: { subdomain: :staging } do
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|
@@ -20,33 +17,29 @@ module QA
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
- }
+ {
+ 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
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
index 39d5fbaba6b..2f66ed697a3 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb
@@ -29,7 +29,7 @@ module QA
Flow::Login.sign_in
add_ci_files
project.visit!
- view_the_last_pipeline
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success')
end
after do
@@ -64,12 +64,6 @@ module QA
end
end
- def view_the_last_pipeline
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_success)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
- end
-
def parent_ci_file
{
file_path: '.gitlab-ci.yml',
diff --git a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
new file mode 100644
index 00000000000..7783dba3fa7
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Composer Repository' do
+ include Runtime::Fixtures
+
+ let(:package_name) { 'my_package' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'composer-package-project'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ let!(:gitlab_address_with_port) do
+ uri = URI.parse(Runtime::Scenario.gitlab_address)
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
+ end
+
+ let(:composer_json_file) do
+ <<~EOF
+ {
+ "name": "#{project.path_with_namespace}/#{package_name}",
+ "description": "Library XY",
+ "type": "library",
+ "license": "GPL-3.0-only",
+ "authors": [
+ {
+ "name": "John Doe",
+ "email": "john@example.com"
+ }
+ ],
+ "require": {}
+ }
+ EOF
+ end
+
+ let(:gitlab_ci_yaml) do
+ <<~YAML
+ publish:
+ image: curlimages/curl:latest
+ stage: build
+ variables:
+ URL: "$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN"
+ script:
+ - version=$([[ -z "$CI_COMMIT_TAG" ]] && echo "branch=$CI_COMMIT_REF_NAME" || echo "tag=$CI_COMMIT_TAG")
+ - insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
+ - response=$(curl -s -w "%{http_code}" $insecure --data $version $URL)
+ - code=$(echo "$response" | tail -n 1)
+ - body=$(echo "$response" | head -n 1)
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ 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: gitlab_ci_yaml
+ },
+ {
+ file_path: 'composer.json',
+ content: composer_json_file
+ }]
+ )
+ end
+
+ project.visit!
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('publish')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a composer package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1088' do
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |package|
+ package.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ aggregate_failures 'package deletion' do
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
new file mode 100644
index 00000000000..2b06ba8646f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Conan Repository' do
+ include Runtime::Fixtures
+
+ let(:package_name) { 'conantest' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'conan-package-project'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ let(:gitlab_address_with_port) do
+ uri = URI.parse(Runtime::Scenario.gitlab_address)
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a conan package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1077' do
+ Flow::Login.sign_in
+
+ 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
+ image: conanio/gcc7
+
+ create_package:
+ stage: deploy
+ script:
+ - "conan remote add gitlab #{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/conan"
+ - "conan new #{package_name}/0.1 -t"
+ - "conan create . mycompany/stable"
+ - "CONAN_LOGIN_USERNAME=ci_user CONAN_PASSWORD=${CI_JOB_TOKEN} conan upload #{package_name}/0.1@mycompany/stable --all --remote=gitlab"
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ }])
+ end
+
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('create_package')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |package|
+ package.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
new file mode 100644
index 00000000000..e163fcbe574
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Maven Repository with Gradle' do
+ include Runtime::Fixtures
+
+ let(:group_id) { 'com.gitlab.qa' }
+ let(:artifact_id) { 'maven_gradle' }
+ let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') }
+ let(:auth_token) do
+ unless Page::Main::Menu.perform(&:signed_in?)
+ Flow::Login.sign_in
+ end
+
+ Resource::PersonalAccessToken.fabricate!.access_token
+ end
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'maven-with-gradle-project'
+ project.initialize_with_readme = true
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ let(:gitlab_address_with_port) do
+ uri = URI.parse(Runtime::Scenario.gitlab_address)
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a maven package via gradle', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1074' do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files([{
+ file_path: '.gitlab-ci.yml',
+ content:
+ <<~YAML
+ deploy:
+ image: gradle:6.5-jdk11
+ script:
+ - 'gradle publish'
+ only:
+ - master
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ },
+ {
+ file_path: 'build.gradle',
+ content:
+ <<~EOF
+ plugins {
+ id 'java'
+ id 'maven-publish'
+ }
+
+ publishing {
+ publications {
+ library(MavenPublication) {
+ groupId '#{group_id}'
+ artifactId '#{artifact_id}'
+ from components.java
+ }
+ }
+ repositories {
+ maven {
+ url "#{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/maven"
+ credentials(HttpHeaderCredentials) {
+ name = "Private-Token"
+ value = "#{auth_token}"
+ }
+ authentication {
+ header(HttpHeaderAuthentication)
+ }
+ }
+ }
+ }
+ EOF
+ }])
+ end
+
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('deploy')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |show|
+ show.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
new file mode 100644
index 00000000000..0b70adf9ff6
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'NuGet Repository' do
+ include Runtime::Fixtures
+
+ let(:package_name) { 'dotnetcore' }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'nuget-package-project'
+ project.template_name = 'dotnetcore'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a nuget package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1073' do
+ Flow::Login.sign_in
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.update_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ image: mcr.microsoft.com/dotnet/core/sdk:3.1
+
+ stages:
+ - deploy
+
+ deploy:
+ stage: deploy
+ script:
+ - dotnet restore -p:Configuration=Release
+ - dotnet build -c Release
+ - dotnet pack -c Release
+ - dotnet nuget add source "$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/packages/nuget/index.json" --name gitlab --username gitlab-ci-token --password $CI_JOB_TOKEN --store-password-in-clear-text
+ - dotnet nuget push "bin/Release/*.nupkg" --source gitlab
+ only:
+ - master
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ }
+ ]
+ )
+ end
+
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('deploy')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |package|
+ package.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
new file mode 100644
index 00000000000..35c41bbb2b0
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'PyPI Repository' do
+ include Runtime::Fixtures
+
+ let(:package_name) { 'mypypipackage' }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'pypi-package-project'
+ end
+ end
+
+ let!(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{project.name}"]
+ runner.executor = :docker
+ runner.project = project
+ end
+ end
+
+ let(:gitlab_address_with_port) do
+ uri = URI.parse(Runtime::Scenario.gitlab_address)
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ 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
+ image: python:latest
+
+ run:
+ script:
+ - pip install twine
+ - python setup.py sdist bdist_wheel
+ - "TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url #{gitlab_address_with_port}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi dist/*"
+ tags:
+ - "runner-for-#{project.name}"
+ YAML
+ },
+ {
+ file_path: 'setup.py',
+ content:
+ <<~EOF
+ import setuptools
+
+ setuptools.setup(
+ name="mypypipackage",
+ version="0.0.1",
+ author="Example Author",
+ author_email="author@example.com",
+ description="A small example package",
+ packages=setuptools.find_packages(),
+ classifiers=[
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ ],
+ python_requires='>=3.6',
+ )
+ EOF
+
+ }])
+ end
+
+ project.visit!
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('run')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a pypi package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1087' do
+ Page::Project::Menu.perform(&:click_packages_link)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_package(package_name)
+ index.click_package(package_name)
+ end
+
+ Page::Project::Packages::Show.perform do |package|
+ package.click_delete
+ end
+
+ Page::Project::Packages::Index.perform do |index|
+ aggregate_failures do
+ expect(index).to have_content("Package deleted successfully")
+ expect(index).to have_no_package(package_name)
+ end
+ end
+ end
+ end
+ end
+end
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 abac4f2b91d..8e61ec4d2d7 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
@@ -77,8 +77,7 @@ module QA
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform(&:click_on_first_job)
Page::Project::Job::Show.perform do |job|
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
index de035e3278a..10795654617 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Release', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/213222', type: :flaky } do
+ RSpec.describe 'Release' do
describe 'Deploy token creation' do
it 'user adds a deploy token', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/419' 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 ece45d093a7..ec26e338b28 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
@@ -27,7 +27,7 @@ module QA
it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/751' do
add_ci_files(success_child_ci_file)
- view_pipelines
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -37,7 +37,7 @@ module QA
it 'parent pipeline fails if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/752' do
add_ci_files(fail_child_ci_file)
- view_pipelines
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -47,12 +47,6 @@ module QA
private
- def view_pipelines
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_completion)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
- end
-
def success_child_ci_file
{
file_path: '.child-ci.yml',
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 38cee0e62ca..d7f5a326b0e 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
@@ -27,7 +27,7 @@ module QA
it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/754' do
add_ci_files(success_child_ci_file)
- view_pipelines
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -37,7 +37,7 @@ module QA
it 'parent pipeline passes even if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/753' do
add_ci_files(fail_child_ci_file)
- view_pipelines
+ Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion')
Page::Project::Pipeline::Show.perform do |parent_pipeline|
expect(parent_pipeline).to have_child_pipeline
@@ -47,12 +47,6 @@ module QA
private
- def view_pipelines
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_completion)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
- end
-
def success_child_ci_file
{
file_path: '.child-ci.yml',
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 6d31780f196..a619ccfad19 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
@@ -54,8 +54,7 @@ module QA
push.commit_message = 'Create Auto DevOps compatible rack application'
end
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
pipeline.click_job('build')
@@ -119,8 +118,7 @@ module QA
end
it 'runs an AutoDevOps pipeline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/444' do
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
+ Flow::Pipeline.visit_latest_pipeline
Page::Project::Pipeline::Show.perform do |pipeline|
expect(pipeline).to have_tag('Auto DevOps')
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index ea0307e58b2..fca46cc2826 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -10,7 +10,7 @@ module QA
super
end
- def refresh
+ def refresh(skip_finished_loading_check: false)
log("refreshing #{current_url}")
super
diff --git a/qa/qa/tools/delete_subgroups.rb b/qa/qa/tools/delete_subgroups.rb
index a05ec735632..903f730a030 100644
--- a/qa/qa/tools/delete_subgroups.rb
+++ b/qa/qa/tools/delete_subgroups.rb
@@ -2,9 +2,9 @@
require_relative '../../qa'
-# This script deletes all subgroups of a group specified by ENV['GROUP_NAME_OR_PATH']
+# This script deletes all subgroups of a group specified by ENV['TOP_LEVEL_GROUP_NAME']
# Required environment variables: GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
-# Optional environment variable: GROUP_NAME_OR_PATH (defaults to 'gitlab-qa-sandbox-group')
+# Optional environment variable: TOP_LEVEL_GROUP_NAME (defaults to 'gitlab-qa-sandbox-group')
# Run `rake delete_subgroups`
module QA
@@ -47,8 +47,9 @@ module QA
end
def fetch_group_id
- group_search_response = get Runtime::API::Request.new(@api_client, "/groups", search: ENV['GROUP_NAME_OR_PATH'] || 'gitlab-qa-sandbox-group').url
- JSON.parse(group_search_response.body).first["id"]
+ group_name = ENV['TOP_LEVEL_GROUP_NAME'] || 'gitlab-qa-sandbox-group'
+ group_search_response = get Runtime::API::Request.new(@api_client, "/groups/#{group_name}" ).url
+ JSON.parse(group_search_response.body)["id"]
end
def fetch_subgroup_ids(group_id, group_pages)
diff --git a/qa/spec/service/docker_run/gitlab_runner_spec.rb b/qa/spec/service/docker_run/gitlab_runner_spec.rb
index db1bb74ca8f..34d95943321 100644
--- a/qa/spec/service/docker_run/gitlab_runner_spec.rb
+++ b/qa/spec/service/docker_run/gitlab_runner_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- describe Service::DockerRun::GitlabRunner do
+ RSpec.describe Service::DockerRun::GitlabRunner do
let(:runner_name) { 'test-runner' }
let(:address) { 'gitlab.test' }
let(:token) { 'abc123' }
diff --git a/qa/spec/service/docker_run/k3s_spec.rb b/qa/spec/service/docker_run/k3s_spec.rb
index 0224b7d6704..e994fbdd30e 100644
--- a/qa/spec/service/docker_run/k3s_spec.rb
+++ b/qa/spec/service/docker_run/k3s_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- describe Service::DockerRun::K3s do
+ RSpec.describe Service::DockerRun::K3s do
describe '#host_name' do
context 'in CI' do
let(:name) { 'k3s-12345' }
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 81730c3ab13..9785d0a9014 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -14,10 +14,13 @@ QA::Runtime::Browser.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) if QA::Runtime::Env.runtime_scenario_attributes
Dir[::File.join(__dir__, "support/helpers/*.rb")].sort.each { |f| require f }
+Dir[::File.join(__dir__, "support/matchers/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "support/shared_contexts/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "support/shared_examples/*.rb")].sort.each { |f| require f }
RSpec.configure do |config|
+ config.include ::Matchers
+
QA::Specs::Helpers::Quarantine.configure_rspec
config.before do |example|
diff --git a/qa/spec/support/matchers/have_file.rb b/qa/spec/support/matchers/have_file.rb
new file mode 100644
index 00000000000..2ae295d5ca2
--- /dev/null
+++ b/qa/spec/support/matchers/have_file.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Matchers
+ module HaveFile
+ RSpec::Matchers.define :have_file do |file|
+ match do |page_object|
+ page_object.has_file?(file)
+ end
+
+ match_when_negated do |page_object|
+ page_object.has_no_file?(file)
+ end
+ end
+ end
+end
diff --git a/qa/spec/support/matchers/have_text.rb b/qa/spec/support/matchers/have_text.rb
new file mode 100644
index 00000000000..4e6fbf1f6d6
--- /dev/null
+++ b/qa/spec/support/matchers/have_text.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Matchers
+ class HaveText
+ def initialize(expected_text, **kwargs)
+ @expected_text = expected_text
+ @kwargs = kwargs
+ end
+
+ def matches?(actual)
+ @actual = wrap(actual)
+ @actual.has_text?(@expected_text, **@kwargs)
+ end
+
+ def does_not_match?(actual)
+ @actual = wrap(actual)
+ @actual.has_no_text?(@expected_text, **@kwargs)
+ end
+
+ def failure_message
+ "expected to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
+ end
+
+ def failure_message_when_negated
+ "expected not to find text \"#{@expected_text}\" in \"#{normalized_actual_text}\""
+ end
+
+ def normalized_actual_text
+ @actual.text.gsub(/\s+/, " ")
+ end
+
+ # From https://github.com/teamcapybara/capybara/blob/fe5940c6afbfe32152df936ce03ad1371ae05354/lib/capybara/rspec/matchers/base.rb#L66
+ def wrap(actual)
+ actual = actual.to_capybara_node if actual.respond_to?(:to_capybara_node)
+ @context_el = if actual.respond_to?(:has_selector?)
+ actual
+ else
+ Capybara.string(actual.to_s)
+ end
+ end
+ end
+
+ def have_text(text, **kwargs) # rubocop:disable Naming/PredicateName
+ HaveText.new(text, **kwargs)
+ end
+
+ alias_method :have_content, :have_text
+end
diff --git a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb b/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
index feaeb78815d..610bf8b9e28 100644
--- a/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
+++ b/qa/spec/support/shared_examples/merge_with_code_owner_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- shared_examples 'code owner merge request' do
+ RSpec.shared_examples 'code owner merge request' do
let(:branch_name) { 'new-branch' }
it 'is approved and merged' do
diff --git a/qa/spec/support/shared_examples/scenario_shared_examples.rb b/qa/spec/support/shared_examples/scenario_shared_examples.rb
index 6e20adbd4ad..637cfb9a05d 100644
--- a/qa/spec/support/shared_examples/scenario_shared_examples.rb
+++ b/qa/spec/support/shared_examples/scenario_shared_examples.rb
@@ -1,73 +1,75 @@
# frozen_string_literal: true
-shared_examples 'a QA scenario class' do
- let(:attributes) { spy('Runtime::Scenario') }
- let(:runner) { spy('Specs::Runner') }
- let(:release) { spy('Runtime::Release') }
- let(:feature) { spy('Runtime::Feature') }
-
- let(:args) { { gitlab_address: 'http://gitlab_address' } }
- let(:named_options) { %w[--address http://gitlab_address] }
- let(:tags) { [] }
- let(:options) { %w[path1 path2] }
-
- before do
- stub_const('QA::Specs::Runner', runner)
- stub_const('QA::Runtime::Release', release)
- stub_const('QA::Runtime::Scenario', attributes)
- stub_const('QA::Runtime::Feature', feature)
-
- allow(runner).to receive(:perform).and_yield(runner)
- allow(QA::Runtime::Address).to receive(:valid?).and_return(true)
- end
+module QA
+ RSpec.shared_examples 'a QA scenario class' do
+ let(:attributes) { spy('Runtime::Scenario') }
+ let(:runner) { spy('Specs::Runner') }
+ let(:release) { spy('Runtime::Release') }
+ let(:feature) { spy('Runtime::Feature') }
+
+ let(:args) { { gitlab_address: 'http://gitlab_address' } }
+ let(:named_options) { %w[--address http://gitlab_address] }
+ let(:tags) { [] }
+ let(:options) { %w[path1 path2] }
+
+ before do
+ stub_const('QA::Specs::Runner', runner)
+ stub_const('QA::Runtime::Release', release)
+ stub_const('QA::Runtime::Scenario', attributes)
+ stub_const('QA::Runtime::Feature', feature)
+
+ allow(runner).to receive(:perform).and_yield(runner)
+ allow(QA::Runtime::Address).to receive(:valid?).and_return(true)
+ end
- it 'responds to perform' do
- expect(subject).to respond_to(:perform)
- end
+ it 'responds to perform' do
+ expect(subject).to respond_to(:perform)
+ end
- it 'sets an address of the subject' do
- subject.perform(args)
+ it 'sets an address of the subject' do
+ subject.perform(args)
- expect(attributes).to have_received(:define).with(:gitlab_address, 'http://gitlab_address').at_least(:once)
- end
+ expect(attributes).to have_received(:define).with(:gitlab_address, 'http://gitlab_address').at_least(:once)
+ end
- it 'performs before hooks only once' do
- subject.perform(args)
+ it 'performs before hooks only once' do
+ subject.perform(args)
- expect(release).to have_received(:perform_before_hooks).once
- end
+ expect(release).to have_received(:perform_before_hooks).once
+ end
- it 'sets tags on runner' do
- subject.perform(args)
+ it 'sets tags on runner' do
+ subject.perform(args)
- expect(runner).to have_received(:tags=).with(tags)
- end
+ expect(runner).to have_received(:tags=).with(tags)
+ end
- context 'specifying RSpec options' do
- it 'sets options on runner' do
- subject.perform(args, *options)
+ context 'specifying RSpec options' do
+ it 'sets options on runner' do
+ subject.perform(args, *options)
- expect(runner).to have_received(:options=).with(options)
+ expect(runner).to have_received(:options=).with(options)
+ end
end
- end
- context 'with named command-line options' do
- it 'converts options to attributes' do
- described_class.launch!(named_options)
+ context 'with named command-line options' do
+ it 'converts options to attributes' do
+ described_class.launch!(named_options)
- args do |k, v|
- expect(attributes).to have_received(:define).with(k, v)
+ args do |k, v|
+ expect(attributes).to have_received(:define).with(k, v)
+ end
end
- end
- it 'raises an error if the option is invalid' do
- expect { described_class.launch!(['--foo']) }.to raise_error(OptionParser::InvalidOption)
- end
+ it 'raises an error if the option is invalid' do
+ expect { described_class.launch!(['--foo']) }.to raise_error(OptionParser::InvalidOption)
+ end
- it 'passes on options after --' do
- expect(described_class).to receive(:perform).with(attributes, *%w[--tag quarantine])
+ it 'passes on options after --' do
+ expect(described_class).to receive(:perform).with(attributes, *%w[--tag quarantine])
- described_class.launch!(named_options.push(*%w[-- --tag quarantine]))
+ described_class.launch!(named_options.push(*%w[-- --tag quarantine]))
+ end
end
end
end