From 7e9c479f7de77702622631cff2628a9c8dcbc627 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 19 Nov 2020 08:27:35 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-6-stable-ee --- qa/Dockerfile | 4 +- qa/Gemfile | 1 - qa/Gemfile.lock | 2 +- qa/bin/qa | 16 ++- qa/qa.rb | 2 + qa/qa/flow/pipeline.rb | 17 +++ qa/qa/page/base.rb | 15 ++- qa/qa/page/component/design_management.rb | 2 +- qa/qa/page/component/issuable/sidebar.rb | 10 +- qa/qa/page/component/note.rb | 87 ++++++++++++- qa/qa/page/component/select2.rb | 10 ++ qa/qa/page/component/snippet.rb | 15 +++ qa/qa/page/dashboard/snippet/edit.rb | 40 ++++++ qa/qa/page/file/shared/commit_message.rb | 8 ++ qa/qa/page/file/show.rb | 21 ++++ qa/qa/page/group/members.rb | 19 +-- qa/qa/page/group/show.rb | 37 ++---- qa/qa/page/layout/flash.rb | 25 ++++ qa/qa/page/main/login.rb | 4 +- qa/qa/page/main/sign_up.rb | 16 ++- qa/qa/page/merge_request/show.rb | 10 +- qa/qa/page/profile/personal_access_tokens.rb | 5 +- qa/qa/page/project/import/github.rb | 4 +- qa/qa/page/project/issue/index.rb | 8 +- qa/qa/page/project/issue/show.rb | 73 +---------- qa/qa/page/project/pipeline/show.rb | 10 ++ qa/qa/page/project/settings/advanced.rb | 3 + .../project/settings/mirroring_repositories.rb | 2 +- qa/qa/page/project/show.rb | 12 +- qa/qa/resource/file.rb | 43 ++++--- qa/qa/resource/group.rb | 2 +- qa/qa/resource/issue.rb | 15 +++ qa/qa/resource/project_imported_from_github.rb | 1 + qa/qa/resource/project_snippet.rb | 8 ++ qa/qa/resource/runner.rb | 2 +- qa/qa/resource/snippet.rb | 32 ++++- qa/qa/resource/user.rb | 2 +- qa/qa/runtime/application_settings.rb | 2 +- qa/qa/runtime/env.rb | 7 +- qa/qa/service/docker_run/gitlab_runner.rb | 2 +- qa/qa/service/praefect_manager.rb | 8 +- .../gitaly/automatic_failover_and_recovery_spec.rb | 12 +- .../push_options_target_branch_spec.rb | 12 ++ .../browser_ui/1_manage/login/2fa_recovery_spec.rb | 2 +- .../1_manage/login/2fa_ssh_recovery_spec.rb | 2 +- .../1_manage/login/log_in_with_2fa_spec.rb | 2 +- .../login/login_via_instance_wide_saml_sso_spec.rb | 4 +- .../browser_ui/1_manage/login/register_spec.rb | 39 +++++- .../1_manage/project/add_project_member_spec.rb | 4 +- .../1_manage/project/create_project_spec.rb | 14 ++- .../1_manage/project/import_github_repo_spec.rb | 85 +++++++------ .../1_manage/project/view_project_activity_spec.rb | 6 +- .../issue/collapse_comments_in_discussions_spec.rb | 2 +- .../browser_ui/2_plan/issue/comment_issue_spec.rb | 4 +- .../browser_ui/2_plan/issue/create_issue_spec.rb | 2 +- .../2_plan/issue/filter_issue_comments_spec.rb | 10 +- .../2_plan/issue/real_time_assignee_spec.rb | 53 ++++++++ .../view_merge_request_merge_ref_diff_spec.rb | 28 +++-- .../create_edit_delete_file_via_web_spec.rb | 57 --------- .../repository/file/create_file_via_web_spec.rb | 31 +++++ .../repository/file/delete_file_via_web_spec.rb | 32 +++++ .../repository/file/edit_file_via_web_spec.rb | 36 ++++++ .../push_mirroring_lfs_over_http_spec.rb | 2 +- .../repository/push_mirroring_over_http_spec.rb | 7 +- .../3_create/snippet/add_file_to_snippet_spec.rb | 54 ++++++++ .../clone_push_pull_personal_snippet_spec.rb | 6 +- .../clone_push_pull_project_snippet_spec.rb | 6 +- .../snippet/delete_file_from_snippet_spec.rb | 60 +++++++++ .../3_create/snippet/share_snippet_spec.rb | 4 +- .../pipeline/create_and_process_pipeline_spec.rb | 3 +- .../include_multiple_files_from_a_project_spec.rb | 138 +++++++++++++++++++++ .../merge_mr_when_pipline_is_blocked_spec.rb | 82 ++++++++++++ ...tenv_variables_to_downstream_via_bridge_spec.rb | 111 +++++++++++++++++ .../pipeline/run_pipeline_via_web_only_spec.rb | 33 ++--- .../trigger_child_pipeline_with_manual_spec.rb | 8 +- .../browser_ui/5_package/composer_registry_spec.rb | 121 ++++++++++++++++++ .../browser_ui/5_package/conan_repository_spec.rb | 88 +++++++++++++ .../5_package/maven_gradle_repository_spec.rb | 126 +++++++++++++++++++ .../browser_ui/5_package/nuget_repository_spec.rb | 92 ++++++++++++++ .../browser_ui/5_package/pypi_repository_spec.rb | 113 +++++++++++++++++ .../deploy_key/clone_using_deploy_key_spec.rb | 3 +- .../deploy_token/add_deploy_token_spec.rb | 2 +- ..._child_pipelines_dependent_relationship_spec.rb | 10 +- ...hild_pipelines_independent_relationship_spec.rb | 10 +- .../create_project_with_auto_devops_spec.rb | 6 +- qa/qa/support/page/logging.rb | 2 +- qa/qa/tools/delete_subgroups.rb | 9 +- qa/spec/service/docker_run/gitlab_runner_spec.rb | 2 +- qa/spec/service/docker_run/k3s_spec.rb | 2 +- qa/spec/spec_helper.rb | 3 + qa/spec/support/matchers/have_file.rb | 15 +++ qa/spec/support/matchers/have_text.rb | 48 +++++++ .../merge_with_code_owner_shared_examples.rb | 2 +- .../shared_examples/scenario_shared_examples.rb | 106 ++++++++-------- 94 files changed, 1875 insertions(+), 438 deletions(-) create mode 100644 qa/qa/flow/pipeline.rb create mode 100644 qa/qa/page/layout/flash.rb create mode 100644 qa/qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb delete mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/create_edit_delete_file_via_web_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/5_package/conan_repository_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb create mode 100644 qa/spec/support/matchers/have_file.rb create mode 100644 qa/spec/support/matchers/have_text.rb (limited to 'qa') 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 " 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 # 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 -- cgit v1.2.3