diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
commit | f64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch) | |
tree | a2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /qa | |
parent | bfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff) | |
download | gitlab-ce-f64a639bcfa1fc2bc89ca7db268f594306edfd7c.tar.gz |
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'qa')
114 files changed, 1607 insertions, 564 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile index 76c81d03071..dda882dc5e6 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -38,6 +38,14 @@ RUN unzip chromedriver_linux64.zip -d /usr/local/bin RUN rm -f chromedriver_linux64.zip ## +# Install root certificate +# +RUN mkdir -p /usr/share/ca-certificates/gitlab +ADD ./qa/tls_certificates/authority/ca.crt /usr/share/ca-certificates/gitlab/ +RUN echo 'gitlab/ca.crt' >> /etc/ca-certificates.conf +RUN chmod -R 644 /usr/share/ca-certificates/gitlab && update-ca-certificates + +## # Install gcloud and kubectl CLI used in Auto DevOps test to create K8s # clusters # diff --git a/qa/Gemfile b/qa/Gemfile index f55d27c159b..21ca945a500 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -21,6 +21,7 @@ gem 'rotp', '~> 3.1.0' gem 'timecop', '~> 0.9.1' gem 'parallel', '~> 1.19' gem 'rspec-parameterized', '~> 0.4.2' +gem 'github_api', '~> 0.18.2' group :development do gem 'pry-byebug', '~> 3.5.1', platform: :mri diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 3b532d90526..4eeaca1f1a6 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -39,19 +39,31 @@ GEM adamantium (~> 0.2.0) equalizer (~> 0.0.9) concurrent-ruby (1.1.7) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.3) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) equalizer (0.0.11) faker (1.9.3) i18n (>= 0.7) + faraday (0.17.3) + multipart-post (>= 1.2, < 3) + github_api (0.18.2) + addressable (~> 2.4) + descendants_tracker (~> 0.0.4) + faraday (~> 0.8) + hashie (~> 3.5, >= 3.5.2) + oauth2 (~> 1.0) gitlab-qa (4.0.0) + hashie (3.6.0) http-accept (1.7.0) http-cookie (1.0.3) domain_name (~> 0.5) i18n (1.8.5) concurrent-ruby (~> 1.0) ice_nine (0.11.2) + jwt (2.2.2) knapsack (1.17.1) rake launchy (2.4.3) @@ -65,10 +77,19 @@ GEM mini_mime (1.0.2) mini_portile2 (2.5.0) minitest (5.14.2) + multi_json (1.15.0) + multi_xml (0.6.0) + multipart-post (2.1.1) netrc (0.11.0) nokogiri (1.11.1) mini_portile2 (~> 2.5.0) racc (~> 1.4) + oauth2 (1.4.4) + faraday (>= 0.8, < 2.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) parallel (1.19.2) parallel_tests (2.29.0) parallel @@ -155,6 +176,7 @@ DEPENDENCIES capybara (~> 3.29.0) capybara-screenshot (~> 1.0.23) faker (~> 1.6, >= 1.6.6) + github_api (~> 0.18.2) gitlab-qa knapsack (~> 1.17) nokogiri (~> 1.11.1) @@ -95,6 +95,7 @@ module QA autoload :Visibility, 'qa/resource/visibility' autoload :ProjectSnippet, 'qa/resource/project_snippet' autoload :Design, 'qa/resource/design' + autoload :RegistryRepository, 'qa/resource/registry_repository' module KubernetesCluster autoload :Base, 'qa/resource/kubernetes_cluster/base' @@ -120,6 +121,7 @@ module QA module Wiki autoload :ProjectPage, 'qa/resource/wiki/project_page' + autoload :GroupPage, 'qa/resource/wiki/group_page' end end @@ -231,6 +233,7 @@ module QA module Settings autoload :General, 'qa/page/group/settings/general' + autoload :PackageRegistries, 'qa/page/group/settings/package_registries' end end @@ -293,6 +296,10 @@ module QA autoload :Show, 'qa/page/project/packages/show' end + module Registry + autoload :Show, 'qa/page/project/registry/show' + end + module Settings autoload :Advanced, 'qa/page/project/settings/advanced' autoload :Main, 'qa/page/project/settings/main' @@ -374,7 +381,6 @@ module QA autoload :Edit, 'qa/page/project/wiki/edit' autoload :Show, 'qa/page/project/wiki/show' autoload :GitAccess, 'qa/page/project/wiki/git_access' - autoload :Sidebar, 'qa/page/project/wiki/sidebar' autoload :List, 'qa/page/project/wiki/list' end @@ -489,6 +495,10 @@ module QA autoload :ProjectSelector, 'qa/page/component/project_selector' autoload :Snippet, 'qa/page/component/snippet' autoload :NewSnippet, 'qa/page/component/new_snippet' + autoload :InviteMembersModal, 'qa/page/component/invite_members_modal' + autoload :Wiki, 'qa/page/component/wiki' + autoload :WikiSidebar, 'qa/page/component/wiki_sidebar' + autoload :WikiPageForm, 'qa/page/component/wiki_page_form' module Issuable autoload :Common, 'qa/page/component/issuable/common' diff --git a/qa/qa/fixtures/designs/testfile.png b/qa/qa/fixtures/designs/testfile.png Binary files differnew file mode 100644 index 00000000000..9693153ca7b --- /dev/null +++ b/qa/qa/fixtures/designs/testfile.png diff --git a/qa/qa/fixtures/designs/update/testfile.png b/qa/qa/fixtures/designs/update/testfile.png Binary files differnew file mode 100644 index 00000000000..fe0ad7f7a1a --- /dev/null +++ b/qa/qa/fixtures/designs/update/testfile.png diff --git a/qa/qa/page/admin/settings/component/ip_limits.rb b/qa/qa/page/admin/settings/component/ip_limits.rb index 9db2ae8ba58..1f9bd113cab 100644 --- a/qa/qa/page/admin/settings/component/ip_limits.rb +++ b/qa/qa/page/admin/settings/component/ip_limits.rb @@ -14,9 +14,9 @@ module QA end def enable_throttles - check_element :throttle_unauthenticated_checkbox - check_element :throttle_authenticated_api_checkbox - check_element :throttle_authenticated_web_checkbox + check_element(:throttle_unauthenticated_checkbox) + check_element(:throttle_authenticated_api_checkbox) + check_element(:throttle_authenticated_web_checkbox) end def save_settings diff --git a/qa/qa/page/admin/settings/component/outbound_requests.rb b/qa/qa/page/admin/settings/component/outbound_requests.rb index 248ea5b6715..c812c05f9c1 100644 --- a/qa/qa/page/admin/settings/component/outbound_requests.rb +++ b/qa/qa/page/admin/settings/component/outbound_requests.rb @@ -19,7 +19,7 @@ module QA private def check_allow_requests_to_local_network_from_services_checkbox - check_element :allow_requests_from_services_checkbox + check_element(:allow_requests_from_services_checkbox) end def click_save_changes_button diff --git a/qa/qa/page/admin/settings/component/performance_bar.rb b/qa/qa/page/admin/settings/component/performance_bar.rb index bc29efb64c0..9e92fa362fb 100644 --- a/qa/qa/page/admin/settings/component/performance_bar.rb +++ b/qa/qa/page/admin/settings/component/performance_bar.rb @@ -12,7 +12,7 @@ module QA end def enable_performance_bar - click_element :enable_performance_bar_checkbox + check_element(:enable_performance_bar_checkbox) Capybara.current_session.driver.browser.manage.add_cookie(name: 'perf_bar_enabled', value: 'true') end diff --git a/qa/qa/page/admin/settings/component/sign_up_restrictions.rb b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb index 9526faf4126..9909155641f 100644 --- a/qa/qa/page/admin/settings/component/sign_up_restrictions.rb +++ b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb @@ -13,13 +13,13 @@ module QA end def require_admin_approval_after_user_signup - check_element :require_admin_approval_after_user_signup_checkbox - click_element :save_changes_button + check_element(:require_admin_approval_after_user_signup_checkbox) + click_element(:save_changes_button) end def disable_signups - uncheck_element :signup_enabled_checkbox - click_element :save_changes_button + uncheck_element(:signup_enabled_checkbox) + click_element(:save_changes_button) end end end diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 99f73bbba48..d1b556b58fb 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -133,9 +133,15 @@ module QA end def check_element(name) + if find_element(name, visible: false).checked? + QA::Runtime::Logger.debug("#{name} is already checked") + + return + end + retry_until(sleep_interval: 1) do - find_element(name).set(true) - checked = find_element(name).checked? + find_element(name, visible: false).click + checked = find_element(name, visible: false).checked? QA::Runtime::Logger.debug(checked ? "#{name} was checked" : "#{name} was not checked") @@ -144,10 +150,19 @@ module QA end def uncheck_element(name) + unless find_element(name, visible: false).checked? + QA::Runtime::Logger.debug("#{name} is already unchecked") + + return + end + retry_until(sleep_interval: 1) do - find_element(name).set(false) + find_element(name, visible: false).click + unchecked = !find_element(name, visible: false).checked? + + QA::Runtime::Logger.debug(unchecked ? "#{name} was unchecked" : "#{name} was not unchecked") - !find_element(name).checked? + unchecked end end diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb new file mode 100644 index 00000000000..fbddb37f15e --- /dev/null +++ b/qa/qa/page/component/invite_members_modal.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module InviteMembersModal + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view 'app/assets/javascripts/invite_members/components/invite_members_modal.vue' do + element :invite_button + element :access_level_dropdown + element :invite_members_modal_content + end + + base.view 'app/assets/javascripts/invite_members/components/group_select.vue' do + element :group_select_dropdown_search_field + end + + base.view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do + element :invite_a_group_button + end + + base.view 'app/assets/javascripts/invite_members/components/invite_members_trigger.vue' do + element :invite_members_button + end + end + + def open_invite_members_modal + click_element :invite_members_button + end + + def open_invite_group_modal + click_element :invite_a_group_button + end + + def add_member(username, access_level = Resource::Members::AccessLevel::DEVELOPER) + open_invite_members_modal + + within_element(:invite_members_modal_content) do + fill_element :access_level_dropdown, with: access_level + + fill_in 'Search for members to invite', with: username + + Support::WaitForRequests.wait_for_requests + + click_button username + + click_element :invite_button + end + + Support::WaitForRequests.wait_for_requests + + page.refresh + end + + def invite_group(group_name, group_access = Resource::Members::AccessLevel::GUEST) + open_invite_group_modal + + fill_element :access_level_dropdown, with: group_access + + click_button 'Select a group' + fill_element :group_select_dropdown_search_field, group_name + + Support::WaitForRequests.wait_for_requests + + click_button group_name + + click_element :invite_button + + Support::WaitForRequests.wait_for_requests + + page.refresh + end + end + end + end +end diff --git a/qa/qa/page/component/issue_board/show.rb b/qa/qa/page/component/issue_board/show.rb index 4c1f03fbe86..d7dfb0757bc 100644 --- a/qa/qa/page/component/issue_board/show.rb +++ b/qa/qa/page/component/issue_board/show.rb @@ -43,6 +43,10 @@ module QA element :focus_mode_button end + view 'app/assets/javascripts/boards/components/config_toggle.vue' do + element :boards_config_button + end + # The `focused_board` method does not use `find_element` with an element defined # with the attribute `data-qa-selector` since such element is not unique when the # `is-focused` class is not set, and it was not possible to find a better solution. @@ -82,6 +86,10 @@ module QA end end + def click_boards_config_button + click_element(:boards_config_button) + end + def click_boards_dropdown_button # The dropdown button comes from the `GlDropdown` component of `@gitlab/ui`, # so it wasn't possible to add a `data-qa-selector` to it. diff --git a/qa/qa/page/component/new_snippet.rb b/qa/qa/page/component/new_snippet.rb index 741a3feb73b..673bc7ba44c 100644 --- a/qa/qa/page/component/new_snippet.rb +++ b/qa/qa/page/component/new_snippet.rb @@ -32,6 +32,10 @@ module QA # This 'element' is here only to ensure the changes in the view source aren't mistakenly changed element :_, "qa_selector = local_assigns.fetch(:qa_selector, '')" # rubocop:disable QA/ElementWithPattern end + + base.view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do + element :visibility_content + end end def fill_title(title) @@ -44,7 +48,7 @@ module QA end def set_visibility(visibility) - choose visibility + click_element(:visibility_content, visibility: visibility) end def fill_file_name(name, file_number = nil) diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb index 50567796bdb..67583f71bf3 100644 --- a/qa/qa/page/component/note.rb +++ b/qa/qa/page/component/note.rb @@ -17,7 +17,6 @@ module QA 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 @@ -146,7 +145,7 @@ module QA def start_discussion(text) fill_element :comment_field, text - click_element :note_dropdown + within_element(:comment_button) { click_button(class: 'dropdown-toggle-split') } click_element :discussion_menu_item click_element :comment_button diff --git a/qa/qa/page/component/wiki.rb b/qa/qa/page/component/wiki.rb new file mode 100644 index 00000000000..92eb25af247 --- /dev/null +++ b/qa/qa/page/component/wiki.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module Wiki + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view 'app/views/shared/wikis/show.html.haml' do + element :wiki_page_title + element :wiki_page_content + element :edit_page_button + end + + base.view 'app/views/shared/wikis/_main_links.html.haml' do + element :new_page_button + element :page_history_button + end + + base.view 'app/views/shared/empty_states/_wikis.html.haml' do + element :create_first_page_link + end + + base.view 'app/views/shared/empty_states/_wikis_layout.html.haml' do + element :svg_content + end + end + + def click_create_your_first_page + # The svg takes a fraction of a second to load after which the + # "Create your first page" button shifts up a bit. This can cause + # webdriver to miss the hit so we wait for the svg to load before + # clicking the button. + within_element(:svg_content) do + has_element?(:js_lazy_loaded) + end + + click_element(:create_first_page_link) + end + + def click_new_page + click_element(:new_page_button) + end + + def click_page_history + click_element(:page_history_button) + end + + def click_edit + click_element(:edit_page_button) + end + + def has_title?(title) + has_element?(:wiki_page_title, title) + end + + def has_content?(content) + has_element?(:wiki_page_content, content) + end + + def has_no_content?(content) + has_no_element?(:wiki_page_content, content) + end + + def has_no_page? + has_element?(:create_first_page_link) + end + end + end + end +end diff --git a/qa/qa/page/component/wiki_page_form.rb b/qa/qa/page/component/wiki_page_form.rb new file mode 100644 index 00000000000..e24b1b67af1 --- /dev/null +++ b/qa/qa/page/component/wiki_page_form.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module WikiPageForm + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view 'app/views/shared/wikis/_form.html.haml' do + element :wiki_title_textbox + element :wiki_content_textarea + element :wiki_message_textbox + element :save_changes_button + element :create_page_button + end + + base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do + element :delete_button + end + end + + def set_title(title) + fill_element(:wiki_title_textbox, title) + end + + def set_content(content) + fill_element(:wiki_content_textarea, content) + end + + def set_message(message) + fill_element(:wiki_message_textbox, message) + end + + def click_save_changes + click_element(:save_changes_button) + end + + def click_create_page + click_element(:create_page_button) + end + + def delete_page + click_element(:delete_button, Page::Modal::DeleteWiki) + Page::Modal::DeleteWiki.perform(&:confirm_deletion) + end + end + end + end +end diff --git a/qa/qa/page/component/wiki_sidebar.rb b/qa/qa/page/component/wiki_sidebar.rb new file mode 100644 index 00000000000..dfb912a1d0b --- /dev/null +++ b/qa/qa/page/component/wiki_sidebar.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module WikiSidebar + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view 'app/views/shared/wikis/_sidebar.html.haml' do + element :clone_repository_link + element :view_all_pages_button + end + + base.view 'app/views/shared/wikis/_sidebar_wiki_page.html.haml' do + element :wiki_page_link + end + + base.view 'app/views/shared/wikis/_wiki_directory.html.haml' do + element :wiki_directory_content + end + end + + def click_clone_repository + click_element(:clone_repository_link) + end + + def click_view_all_pages + click_element(:view_all_pages_button) + end + + def click_page_link(page_title) + click_element(:wiki_page_link, page_name: page_title) + end + + def has_page_listed?(page_title) + has_element?(:wiki_page_link, page_name: page_title) + end + + def has_directory?(directory) + has_element?(:wiki_directory_content, text: directory) + end + end + end + end +end diff --git a/qa/qa/page/dashboard/snippet/edit.rb b/qa/qa/page/dashboard/snippet/edit.rb index 40add146e97..cd8ac77ae04 100644 --- a/qa/qa/page/dashboard/snippet/edit.rb +++ b/qa/qa/page/dashboard/snippet/edit.rb @@ -18,13 +18,17 @@ module QA element :delete_file_button end + view 'app/assets/javascripts/snippets/components/snippet_visibility_edit.vue' do + element :visibility_content + end + def add_to_file_content(content) text_area.set content text_area.has_text?(content) # wait for changes to take effect end def change_visibility_to(visibility_type) - choose(visibility_type) + click_element(:visibility_content, visibility: visibility_type) end def click_add_file diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb index 87423ae9d75..68a169d5a7f 100644 --- a/qa/qa/page/group/members.rb +++ b/qa/qa/page/group/members.rb @@ -4,18 +4,13 @@ module QA module Page module Group class Members < Page::Base - include QA::Page::Component::Select2 + include Page::Component::InviteMembersModal include Page::Component::UsersSelect view 'app/assets/javascripts/vue_shared/components/remove_member_modal.vue' do element :remove_member_modal_content end - view 'app/views/shared/members/_invite_member.html.haml' do - element :member_select_field - element :invite_member_button - end - view 'app/assets/javascripts/pages/groups/group_members/index.js' do element :member_row element :groups_list @@ -32,31 +27,9 @@ module QA end view 'app/views/groups/group_members/index.html.haml' do - element :invite_group_tab element :groups_list_tab end - view 'app/views/shared/members/_invite_group.html.haml' do - element :group_select_field - element :invite_group_button - end - - def select_group(group_name) - click_element :group_select_field - search_and_select(group_name) - end - - def invite_group(group_name) - click_element :invite_group_tab - select_group(group_name) - click_element :invite_group_button - end - - def add_member(username) - select_user :member_select_field, username - click_element :invite_member_button - end - def update_access_level(username, access_level) within_element(:member_row, text: username) do click_element :access_level_dropdown diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb index 7689dd7e5c8..10c0ee1f0a9 100644 --- a/qa/qa/page/group/menu.rb +++ b/qa/qa/page/group/menu.rb @@ -14,6 +14,10 @@ module QA element :group_settings_item end + view 'app/views/groups/sidebar/_packages_settings.html.haml' do + element :group_package_settings_link + end + view 'app/views/layouts/nav/sidebar/_analytics_links.html.haml' do element :analytics_link element :analytics_sidebar_submenu @@ -55,6 +59,15 @@ module QA end end + def go_to_package_settings + scroll_to_element(:group_settings_item) + hover_element(:group_settings_item) do + within_submenu(:group_sidebar_submenu) do + click_element(:group_package_settings_link) + end + end + end + private def hover_issues diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb index 8f5267c3362..ced8bd5c812 100644 --- a/qa/qa/page/group/settings/general.rb +++ b/qa/qa/page/group/settings/general.rb @@ -54,57 +54,57 @@ module QA end def set_lfs_enabled - expand_content :permission_lfs_2fa_content - check_element :lfs_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + check_element(:lfs_checkbox) + click_element(:save_permissions_changes_button) end def set_lfs_disabled - expand_content :permission_lfs_2fa_content - uncheck_element :lfs_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + uncheck_element(:lfs_checkbox) + click_element(:save_permissions_changes_button) end def set_request_access_enabled - expand_content :permission_lfs_2fa_content - check_element :request_access_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + check_element(:request_access_checkbox) + click_element(:save_permissions_changes_button) end def set_request_access_disabled - expand_content :permission_lfs_2fa_content - uncheck_element :request_access_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + uncheck_element(:request_access_checkbox) + click_element(:save_permissions_changes_button) end def set_require_2fa_enabled - expand_content :permission_lfs_2fa_content - check_element :require_2fa_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + check_element(:require_2fa_checkbox) + click_element(:save_permissions_changes_button) end def set_require_2fa_disabled - expand_content :permission_lfs_2fa_content - uncheck_element :require_2fa_checkbox - click_element :save_permissions_changes_button + expand_content(:permission_lfs_2fa_content) + uncheck_element(:require_2fa_checkbox) + click_element(:save_permissions_changes_button) end def set_project_creation_level(value) - expand_content :permission_lfs_2fa_content + expand_content(:permission_lfs_2fa_content) select_element(:project_creation_level_dropdown, value) - click_element :save_permissions_changes_button + click_element(:save_permissions_changes_button) end def toggle_request_access - expand_content :permission_lfs_2fa_content + expand_content(:permission_lfs_2fa_content) if find_element(:request_access_checkbox).checked? - uncheck_element :request_access_checkbox + uncheck_element(:request_access_checkbox) else - check_element :request_access_checkbox + check_element(:request_access_checkbox) end - click_element :save_permissions_changes_button + click_element(:save_permissions_changes_button) end end end diff --git a/qa/qa/page/group/settings/package_registries.rb b/qa/qa/page/group/settings/package_registries.rb new file mode 100644 index 00000000000..ee5ca849a47 --- /dev/null +++ b/qa/qa/page/group/settings/package_registries.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module QA + module Page + module Group + module Settings + class PackageRegistries < QA::Page::Base + include ::QA::Page::Settings::Common + + view 'app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue' do + element :package_registry_settings_content + end + + view 'app/assets/javascripts/packages_and_registries/settings/group/components/maven_settings.vue' do + element :allow_duplicates_toggle + element :allow_duplicates_label + end + + def set_allow_duplicates_disabled + expand_content :package_registry_settings_content do + click_element(:allow_duplicates_toggle) if duplicates_enabled? + end + end + + def set_allow_duplicates_enabled + expand_content :package_registry_settings_content do + click_element(:allow_duplicates_toggle) if duplicates_disabled? + end + end + + def duplicates_enabled? + has_element?(:allow_duplicates_label, text: 'Allow duplicates') + end + + def duplicates_disabled? + has_element?(:allow_duplicates_label, text: 'Do not allow duplicates') + end + end + end + end + end +end diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index 265e2b7573c..048119d65cb 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -52,7 +52,7 @@ module QA using_wait_time 0 do set_initial_password_if_present - raise NotImplementedError if Runtime::User.ldap_user? && user&.credentials_given? + raise 'If an LDAP user is provided, it must be used for sign-in', QA::Resource::User::InvalidUserError if Runtime::User.ldap_user? && user && user.username != Runtime::User.ldap_username if Runtime::User.ldap_user? sign_in_using_ldap_credentials(user: user || Runtime::User) diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 18676807fd6..0b6a3085a3a 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -9,8 +9,8 @@ module QA view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue' do element :download_dropdown - element :download_email_patches - element :download_plain_diff + element :download_email_patches_menu_item + element :download_plain_diff_menu_item element :open_in_web_ide_button end @@ -21,9 +21,13 @@ module QA view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do element :merge_button - element :fast_forward_message, 'Fast-forward merge without a merge commit' # rubocop:disable QA/ElementWithPattern + element :fast_forward_message_content element :merge_moment_dropdown - element :merge_immediately_option + element :merge_immediately_menu_item + end + + view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue' do + element :merge_request_status_content end view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue' do @@ -36,7 +40,7 @@ module QA view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue' do element :mr_rebase_button - element :no_fast_forward_message, 'Fast-forward merge is not possible' # rubocop:disable QA/ElementWithPattern + element :no_fast_forward_message_content end view 'app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue' do @@ -64,7 +68,8 @@ module QA end view 'app/assets/javascripts/diffs/components/inline_diff_table_row.vue' do - element :new_diff_line + element :diff_comment_button + element :new_diff_line_link end view 'app/views/projects/merge_requests/_mr_title.html.haml' do @@ -72,11 +77,11 @@ module QA end view 'app/assets/javascripts/batch_comments/components/publish_button.vue' do - element :submit_review + element :submit_review_button end view 'app/assets/javascripts/batch_comments/components/review_bar.vue' do - element :review_bar + element :review_bar_content end view 'app/assets/javascripts/notes/components/note_form.vue' do @@ -124,18 +129,18 @@ module QA end def submit_pending_reviews - within_element(:review_bar) do + within_element(:review_bar_content) do click_element(:review_preview_toggle) - click_element(:submit_review) + click_element(:submit_review_button) # After clicking the button, wait for it to disappear # before moving on to the next part of the test - has_no_element?(:submit_review) + has_no_element?(:submit_review_button) end end def discard_pending_reviews - within_element(:review_bar) do + within_element(:review_bar_content) do click_element(:discard_review) end click_element(:modal_delete_pending_comments) @@ -154,8 +159,8 @@ module QA wait_until(sleep_interval: 5) do has_css?('a[data-linenumber="1"]') end - all_elements(:new_diff_line, minimum: 1).first.hover - click_element(:diff_comment) + all_elements(:new_diff_line_link, minimum: 1).first.hover + click_element(:diff_comment_button) fill_element(:reply_field, text) end @@ -179,7 +184,11 @@ module QA end def fast_forward_possible? - has_no_text?('Fast-forward merge is not possible') + has_element?(:fast_forward_message_content) + end + + def fast_forward_not_possible? + has_element?(:no_fast_forward_message_content) end def has_file?(file_name) @@ -220,12 +229,12 @@ module QA !find_element(:squash_checkbox).disabled? end + # TODO: Fix workaround for data-qa-selector failure click_element(:squash_checkbox) end def merge! - wait_until_ready_to_merge - click_element(:merge_button) + try_to_merge! finished_loading? raise "Merge did not appear to be successful" unless merged? @@ -233,11 +242,21 @@ module QA def merge_immediately! click_element(:merge_moment_dropdown) - click_element(:merge_immediately_option) + click_element(:merge_immediately_menu_item) + end + + def merge_when_pipeline_succeeds! + wait_until_ready_to_merge + + click_element(:merge_button, text: 'Merge when pipeline succeeds') end def merged? - has_element?(:merged_status_content, text: 'The changes were merged into', wait: 60) + # Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042 + # To remove page refresh logic if possible + retry_until(max_attempts: 3, reload: true) do + has_element?(:merged_status_content, text: 'The changes were merged into', wait: 20) + end end # Check if the MR is able to be merged @@ -249,6 +268,10 @@ module QA has_element?(:merge_button, disabled: false) end + def merge_request_status + find_element(:merge_request_status_content).text + end + # Waits up 60 seconds and raises an error if unable to merge def wait_until_ready_to_merge has_element?(:merge_button) @@ -273,26 +296,29 @@ module QA click_element(:mr_rebase_button) success = wait_until do - has_text?('Fast-forward merge without a merge commit') + fast_forward_possible? end raise "Rebase did not appear to be successful" unless success end def try_to_merge! + # Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042 + # To remove page refresh logic if possible wait_until_ready_to_merge + wait_until { !find_element(:merge_button).has_text?("when pipeline succeeds") } click_element(:merge_button) end def view_email_patches click_element(:download_dropdown) - visit_link_in_element(:download_email_patches) + visit_link_in_element(:download_email_patches_menu_item) end def view_plain_diff click_element(:download_dropdown) - visit_link_in_element(:download_plain_diff) + visit_link_in_element(:download_plain_diff_menu_item) end def wait_for_merge_request_error_message @@ -315,7 +341,7 @@ module QA def add_suggestion_to_diff(suggestion, line) find("a[data-linenumber='#{line}']").hover - click_element(:diff_comment) + click_element(:diff_comment_button) click_element(:suggestion_button) initial_content = find_element(:reply_field).value fill_element(:reply_field, '') diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb index 6890c7de9f8..58c82fa14c1 100644 --- a/qa/qa/page/project/import/github.rb +++ b/qa/qa/page/project/import/github.rb @@ -17,28 +17,34 @@ module QA element :project_namespace_select element :project_path_field element :import_button + element :project_path_content + element :go_to_project_button end def add_personal_access_token(personal_access_token) + # If for some reasons this process is retried, user cannot re-enter github token in the same group + # In this case skip this step and proceed to import project row + return unless has_element?(:personal_access_token_field) + fill_element(:personal_access_token_field, personal_access_token) click_element(:authenticate_button) finished_loading? end def import!(full_path, name) - choose_test_namespace(full_path) - set_path(full_path, name) - import_project(full_path) - wait_for_success + unless already_imported(full_path) + choose_test_namespace(full_path) + set_path(full_path, name) + import_project(full_path) + wait_for_success + end + + go_to_project(name) end private def within_repo_path(full_path) - wait_until(reload: false) do - has_element?(:project_import_row, text: full_path) - end - project_import_row = find_element(:project_import_row, text: full_path) within(project_import_row) do @@ -68,9 +74,22 @@ module QA def wait_for_success # TODO: set reload:false and remove skip_finished_loading_check_on_refresh when - # https://gitlab.com/gitlab-org/gitlab/-/issues/231542 is fixed + # https://gitlab.com/gitlab-org/gitlab/-/issues/292861 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) + page.has_no_content?('Importing 1 repository', wait: 3.0) + end + end + + def go_to_project(name) + Page::Main::Menu.perform(&:go_to_projects) + Page::Dashboard::Projects.perform do |dashboard| + dashboard.go_to_project(name) + end + end + + def already_imported(full_path) + within_repo_path(full_path) do + has_element?(:project_path_content) && has_element?(:go_to_project_button) end end end diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb index e85d10e4eb8..10ddd52719a 100644 --- a/qa/qa/page/project/issue/index.rb +++ b/qa/qa/page/project/issue/index.rb @@ -15,18 +15,14 @@ module QA element :avatar_counter_content end - view 'app/views/shared/issuable/csv_export/_button.html.haml' do - element :export_as_csv_button - end - - view 'app/views/shared/issuable/csv_export/_modal.html.haml' do - element :export_issues_button + view 'app/assets/javascripts/issuable/components/csv_export_modal.vue' do element :export_issuable_modal end - view 'app/views/projects/issues/import_csv/_button.html.haml' do - element :import_issues_button + view 'app/assets/javascripts/issuable/components/csv_import_export_buttons.vue' do + element :export_as_csv_button element :import_from_jira_link + element :import_issues_dropdown end view 'app/views/shared/issuable/_nav.html.haml' do @@ -60,7 +56,7 @@ module QA def click_import_issues_dropdown # When there are no issues, the image that loads causes the buttons to jump has_loaded_all_images? - click_element(:import_issues_button) + click_element(:import_issues_dropdown) end def export_issues_modal diff --git a/qa/qa/page/project/members.rb b/qa/qa/page/project/members.rb index 7e722e02ad0..09264d95aed 100644 --- a/qa/qa/page/project/members.rb +++ b/qa/qa/page/project/members.rb @@ -4,21 +4,18 @@ module QA module Page module Project class Members < Page::Base - include QA::Page::Component::Select2 - - view 'app/views/shared/members/_invite_member.html.haml' do - element :member_select_field - element :invite_member_button - end + include QA::Page::Component::InviteMembersModal view 'app/views/projects/project_members/index.html.haml' do - element :invite_group_tab element :groups_list_tab end - view 'app/views/shared/members/_invite_group.html.haml' do - element :group_select_field - element :invite_group_button + view 'app/assets/javascripts/invite_members/components/invite_group_trigger.vue' do + element :invite_a_group_button + end + + view 'app/assets/javascripts/invite_members/components/invite_members_trigger.vue' do + element :invite_members_button end view 'app/assets/javascripts/pages/projects/project_members/index.js' do @@ -33,25 +30,7 @@ module QA element :remove_group_link_modal_content end - def select_group(group_name) - click_element :group_select_field - search_and_select(group_name) - end - - def invite_group(group_name) - click_element :invite_group_tab - select_group(group_name) - click_element :invite_group_button - end - - def add_member(username) - click_element :member_select_field - search_and_select username - click_element :invite_member_button - end - def remove_group(group_name) - click_element :invite_group_tab click_element :groups_list_tab within_element(:group_row, text: group_name) do diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb index 7e296528795..d1033dbfca9 100644 --- a/qa/qa/page/project/new.rb +++ b/qa/qa/page/project/new.rb @@ -68,7 +68,7 @@ module QA end def enable_initialize_with_readme - check_element :initialize_with_readme_checkbox + check_element(:initialize_with_readme_checkbox) end end end diff --git a/qa/qa/page/project/operations/kubernetes/add_existing.rb b/qa/qa/page/project/operations/kubernetes/add_existing.rb index 1b9a451c47d..59f59ca9966 100644 --- a/qa/qa/page/project/operations/kubernetes/add_existing.rb +++ b/qa/qa/page/project/operations/kubernetes/add_existing.rb @@ -36,7 +36,7 @@ module QA end def uncheck_rbac! - uncheck_element :rbac_checkbox + uncheck_element(:rbac_checkbox) end end end diff --git a/qa/qa/page/project/operations/kubernetes/index.rb b/qa/qa/page/project/operations/kubernetes/index.rb index 114e3ddd46a..ca41dddaca2 100644 --- a/qa/qa/page/project/operations/kubernetes/index.rb +++ b/qa/qa/page/project/operations/kubernetes/index.rb @@ -7,11 +7,11 @@ module QA module Kubernetes class Index < Page::Base view 'app/views/clusters/clusters/_empty_state.html.haml' do - element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Integrate with a cluster certificate')" # rubocop:disable QA/ElementWithPattern + element :add_kubernetes_cluster_link end def add_kubernetes_cluster - click_on 'Connect cluster with certificate' + click_element :add_kubernetes_cluster_link end def has_cluster?(cluster) diff --git a/qa/qa/page/project/registry/show.rb b/qa/qa/page/project/registry/show.rb new file mode 100644 index 00000000000..dffdb9eebba --- /dev/null +++ b/qa/qa/page/project/registry/show.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module QA + module Page + module Project + module Registry + class Show < QA::Page::Base + view 'app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue' do + element :registry_image_content + end + + view 'app/assets/javascripts/registry/explorer/components/details_page/tags_list_row.vue' do + element :tag_delete_button + end + + def has_registry_repository?(name) + find_element(:registry_image_content, text: name) + end + + def click_on_image(name) + click_element(:registry_image_content, text: name) + end + + def has_tag?(tag_name) + has_button?(tag_name) + end + + def has_no_tag?(tag_name) + has_no_button?(tag_name) + end + + def click_delete + click_element(:tag_delete_button) + find_button('Confirm').click + end + end + end + end + end +end diff --git a/qa/qa/page/project/settings/auto_devops.rb b/qa/qa/page/project/settings/auto_devops.rb index 827d5b072c3..9dffa010805 100644 --- a/qa/qa/page/project/settings/auto_devops.rb +++ b/qa/qa/page/project/settings/auto_devops.rb @@ -11,8 +11,8 @@ module QA end def enable_autodevops - check_element :enable_autodevops_checkbox - click_element :save_changes_button + check_element(:enable_autodevops_checkbox) + click_element(:save_changes_button) end end end diff --git a/qa/qa/page/project/settings/ci_variables.rb b/qa/qa/page/project/settings/ci_variables.rb index f2ced668a60..2b8fad64afb 100644 --- a/qa/qa/page/project/settings/ci_variables.rb +++ b/qa/qa/page/project/settings/ci_variables.rb @@ -10,7 +10,6 @@ module QA view 'app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue' do element :ci_variable_key_field element :ci_variable_value_field - element :ci_variable_masked_checkbox element :ci_variable_save_button element :ci_variable_delete_button end diff --git a/qa/qa/page/project/settings/incidents.rb b/qa/qa/page/project/settings/incidents.rb index 9b523e2aa9e..610129851d9 100644 --- a/qa/qa/page/project/settings/incidents.rb +++ b/qa/qa/page/project/settings/incidents.rb @@ -13,7 +13,7 @@ module QA end def enable_issues_for_incidents - check_element :create_issue_checkbox + check_element(:create_issue_checkbox) end def select_issue_template(template) diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb index 4db66543a25..48af635bb79 100644 --- a/qa/qa/page/project/settings/main.rb +++ b/qa/qa/page/project/settings/main.rb @@ -11,8 +11,9 @@ module QA include Component::Breadcrumbs view 'app/views/projects/edit.html.haml' do - element :advanced_settings - element :merge_request_settings + element :advanced_settings_content + element :merge_request_settings_content + element :visibility_features_permissions_content end view 'app/views/projects/settings/_general.html.haml' do @@ -20,10 +21,6 @@ module QA element :save_naming_topics_avatar_button end - view 'app/views/projects/edit.html.haml' do - element :visibility_features_permissions_content - end - def rename_project_to(name) fill_project_name(name) click_save_changes @@ -38,13 +35,13 @@ module QA end def expand_advanced_settings(&block) - expand_content(:advanced_settings) do + expand_content(:advanced_settings_content) do Advanced.perform(&block) end end def expand_merge_requests_settings(&block) - expand_content(:merge_request_settings) do + expand_content(:merge_request_settings_content) do MergeRequest.perform(&block) end end diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb index ec8d73df1b3..fe5d629effe 100644 --- a/qa/qa/page/project/settings/merge_request.rb +++ b/qa/qa/page/project/settings/merge_request.rb @@ -8,11 +8,11 @@ module QA include QA::Page::Settings::Common view 'app/views/projects/edit.html.haml' do - element :save_merge_request_changes + element :save_merge_request_changes_button end view 'app/views/projects/_merge_request_merge_method_settings.html.haml' do - element :radio_button_merge_ff + element :merge_ff_radio_button end view 'app/views/projects/_merge_request_merge_checks_settings.html.haml' do @@ -20,16 +20,16 @@ module QA end def click_save_changes - click_element :save_merge_request_changes + click_element :save_merge_request_changes_button end def enable_ff_only - click_element :radio_button_merge_ff + click_element :merge_ff_radio_button click_save_changes end def enable_merge_if_all_disscussions_are_resolved - click_element :allow_merge_if_all_discussions_are_resolved_checkbox + check_element(:allow_merge_if_all_discussions_are_resolved_checkbox) click_save_changes end end diff --git a/qa/qa/page/project/sub_menus/packages.rb b/qa/qa/page/project/sub_menus/packages.rb index 9ea045a99f5..46eae01e10d 100644 --- a/qa/qa/page/project/sub_menus/packages.rb +++ b/qa/qa/page/project/sub_menus/packages.rb @@ -22,6 +22,25 @@ module QA click_element :packages_link end end + + def go_to_container_registry + hover_registry do + within_submenu do + click_link('Container Registry') + end + end + end + + private + + def hover_registry + within_sidebar do + scroll_to_element(:packages_link) + find_element(:packages_link).hover + + yield + end + end end end end diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb index 47274c8db54..b5058bacccd 100644 --- a/qa/qa/page/project/sub_menus/settings.rb +++ b/qa/qa/page/project/sub_menus/settings.rb @@ -25,7 +25,7 @@ module QA def go_to_ci_cd_settings hover_settings do within_submenu do - click_link('CI / CD') + click_link('CI/CD') end end end diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb index 45c46004790..fd68ac0de16 100644 --- a/qa/qa/page/project/web_ide/edit.rb +++ b/qa/qa/page/project/web_ide/edit.rb @@ -44,10 +44,6 @@ module QA element :commit_button end - view 'app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue' do - element :start_new_mr_checkbox - end - view 'app/assets/javascripts/ide/components/repo_editor.vue' do element :editor_container end diff --git a/qa/qa/page/project/wiki/edit.rb b/qa/qa/page/project/wiki/edit.rb index 6f3be904eb3..70aa10cc43e 100644 --- a/qa/qa/page/project/wiki/edit.rb +++ b/qa/qa/page/project/wiki/edit.rb @@ -5,44 +5,8 @@ module QA module Project module Wiki class Edit < Base - include Wiki::Sidebar - - view 'app/views/shared/wikis/_form.html.haml' do - element :wiki_title_textbox - element :wiki_content_textarea - element :wiki_message_textbox - element :save_changes_button - element :create_page_button - end - - view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do - element :delete_button - end - - def set_title(title) - fill_element :wiki_title_textbox, title - end - - def set_content(content) - fill_element :wiki_content_textarea, content - end - - def set_message(message) - fill_element :wiki_message_textbox, message - end - - def click_save_changes - click_element :save_changes_button - end - - def click_create_page - click_element :create_page_button - end - - def delete_page - click_element :delete_button, Page::Modal::DeleteWiki - Page::Modal::DeleteWiki.perform(&:confirm_deletion) - end + include Page::Component::WikiPageForm + include Page::Component::WikiSidebar end end end diff --git a/qa/qa/page/project/wiki/show.rb b/qa/qa/page/project/wiki/show.rb index d9e0783a581..f3573e3cdd3 100644 --- a/qa/qa/page/project/wiki/show.rb +++ b/qa/qa/page/project/wiki/show.rb @@ -5,67 +5,9 @@ module QA module Project module Wiki class Show < Base - include Wiki::Sidebar - include Component::LazyLoader - - view 'app/views/shared/wikis/show.html.haml' do - element :wiki_page_title - element :wiki_page_content - element :edit_page_button - end - - view 'app/views/shared/wikis/_main_links.html.haml' do - element :new_page_button - element :page_history_button - end - - view 'app/views/shared/empty_states/_wikis.html.haml' do - element :create_first_page_link - end - - view 'app/views/shared/empty_states/_wikis_layout.html.haml' do - element :svg_content - end - - def click_create_your_first_page - # The svg takes a fraction of a second to load after which the - # "Create your first page" button shifts up a bit. This can cause - # webdriver to miss the hit so we wait for the svg to load before - # clicking the button. - within_element(:svg_content) do - has_element? :js_lazy_loaded - end - - click_element :create_first_page_link - end - - def click_new_page - click_element(:new_page_button) - end - - def click_page_history - click_element(:page_history_button) - end - - def click_edit - click_element(:edit_page_button) - end - - def has_title?(title) - has_element?(:wiki_page_title, title) - end - - def has_content?(content) - has_element?(:wiki_page_content, content) - end - - def has_no_content?(content) - has_no_element?(:wiki_page_content, content) - end - - def has_no_page? - has_element? :create_first_page_link - end + include Page::Component::Wiki + include Page::Component::WikiSidebar + include Page::Component::LazyLoader end end end diff --git a/qa/qa/page/project/wiki/sidebar.rb b/qa/qa/page/project/wiki/sidebar.rb deleted file mode 100644 index 3e1edcbbefb..00000000000 --- a/qa/qa/page/project/wiki/sidebar.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -module QA - module Page - module Project - module Wiki - module Sidebar - extend QA::Page::PageConcern - - def self.included(base) - super - - base.view 'app/views/shared/wikis/_sidebar.html.haml' do - element :clone_repository_link - element :view_all_pages_button - end - - base.view 'app/views/shared/wikis/_sidebar_wiki_page.html.haml' do - element :wiki_page_link - end - - base.view 'app/views/shared/wikis/_wiki_directory.html.haml' do - element :wiki_directory_content - end - end - - def click_clone_repository - click_element(:clone_repository_link) - end - - def click_view_all_pages - click_element(:view_all_pages_button) - end - - def click_page_link(page_title) - click_element :wiki_page_link, page_name: page_title - end - - def has_page_listed?(page_title) - has_element? :wiki_page_link, page_name: page_title - end - - def has_directory?(directory) - has_element? :wiki_directory_content, text: directory - end - end - end - end - end -end diff --git a/qa/qa/resource/clusters/agent_token.rb b/qa/qa/resource/clusters/agent_token.rb index 6a5e861b650..3286f46cdb2 100644 --- a/qa/qa/resource/clusters/agent_token.rb +++ b/qa/qa/resource/clusters/agent_token.rb @@ -32,7 +32,7 @@ module QA def api_post_body <<~GQL mutation createToken { - clusterAgentTokenCreate(input: { clusterAgentId: "gid://gitlab/Clusters::Agent/#{agent.id}" }) { + clusterAgentTokenCreate(input: { clusterAgentId: "gid://gitlab/Clusters::Agent/#{agent.id}" name: "token-#{agent.id}" }) { secret # This is the value you need to use on the next step token { createdAt diff --git a/qa/qa/resource/members.rb b/qa/qa/resource/members.rb index a88980f26d8..c8f9feeca15 100644 --- a/qa/qa/resource/members.rb +++ b/qa/qa/resource/members.rb @@ -26,10 +26,23 @@ module QA JSON.parse(get(Runtime::API::Request.new(api_client, api_members_path).url).body) end + def invite_group(group, access_level = AccessLevel::GUEST) + Support::Retrier.retry_until do + QA::Runtime::Logger.debug(%Q[Sharing #{self.class.name} with #{group.name}]) + + response = post Runtime::API::Request.new(api_client, api_share_path).url, { group_id: group.id, group_access: access_level } + response.code == QA::Support::Api::HTTP_STATUS_CREATED + end + end + def api_members_path "#{api_get_path}/members" end + def api_share_path + "#{api_get_path}/share" + end + class AccessLevel NO_ACCESS = 0 MINIMAL_ACCESS = 5 diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb index fd5f454f134..fb450a61c9a 100644 --- a/qa/qa/resource/merge_request.rb +++ b/qa/qa/resource/merge_request.rb @@ -75,7 +75,7 @@ module QA Page::MergeRequest::New.perform do |new_page| new_page.fill_title(@title) new_page.choose_template(@template) if @template - new_page.fill_description(@description) unless @template + new_page.fill_description(@description) if @description && !@template new_page.choose_milestone(@milestone) if @milestone new_page.assign_to_me if @assignee == 'me' labels.each do |label| @@ -140,6 +140,14 @@ module QA end end + def reload! + # Refabricate so that we can return a new object with updated attributes + self.class.fabricate_via_api! do |resource| + resource.project = project + resource.id = api_resource[:iid] + end + end + private def transform_api_resource(api_resource) diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb index 6b2301ba916..59ae8f4de7a 100644 --- a/qa/qa/resource/personal_access_token.rb +++ b/qa/qa/resource/personal_access_token.rb @@ -4,17 +4,59 @@ require 'date' module QA module Resource - ## - # Create a personal access token that can be used by the api - # class PersonalAccessToken < Base attr_accessor :name - attribute :access_token do + # The user for which the personal access token is to be created + # This *could* be different than the api_client.user or the api_user provided by the QA::Resource::ApiFabricator module + attr_writer :user + + attribute :token do Page::Profile::PersonalAccessTokens.perform(&:created_access_token) end + # Only Admins can create PAT via the API. + # If Runtime::Env.admin_personal_access_token is provided, fabricate via the API, + # else, fabricate via the browser. + def fabricate_via_api! + if Runtime::Env.admin_personal_access_token && !@user.nil? + self.api_client = Runtime::API::Client.as_admin + + super + else + fabricate! + end + end + + # When a user is not provided, use default user + def user + @user || Resource::User.default + end + + def api_post_path + "/users/#{user.api_resource[:id]}/personal_access_tokens" + end + + def api_get_path + '/personal_access_tokens' + end + + def api_post_body + { + name: name || 'api-test-token', + scopes: ["api"] + } + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + def fabricate! + Flow::Login.sign_in_unless_signed_in(as: user) + Page::Main::Menu.perform(&:click_edit_profile_link) Page::Profile::Menu.perform(&:click_access_tokens) @@ -22,7 +64,7 @@ module QA token_page.fill_token_name(name || 'api-test-token') token_page.check_api # Expire in 2 days just in case the token is created just before midnight - token_page.fill_expiry_date(Date.today + 2) + token_page.fill_expiry_date(Time.now.utc.to_date + 2) token_page.click_create_token_button end end diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index efb6c2c0591..23e2ec07491 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -151,6 +151,10 @@ module QA "#{api_get_path}/runners" end + def api_registry_repositories_path + "#{api_get_path}/registry/repositories" + end + def api_commits_path "#{api_get_path}/repository/commits" end @@ -256,6 +260,12 @@ module QA parse_body(response) end + def registry_repositories + response = get Runtime::API::Request.new(api_client, "#{api_registry_repositories_path}").url + + parse_body(response) + end + def repository_branches parse_body(get(Runtime::API::Request.new(api_client, api_repository_branches_path).url)) end @@ -272,10 +282,6 @@ module QA parse_body(get(Runtime::API::Request.new(api_client, api_pipelines_path).url)) end - def share_with_group(invitee, access_level = Resource::Members::AccessLevel::DEVELOPER) - post Runtime::API::Request.new(api_client, "/projects/#{id}/share").url, { group_id: invitee.id, group_access: access_level } - end - private def transform_api_resource(api_resource) diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb index 0b817b345fd..b06a7fe4e3d 100644 --- a/qa/qa/resource/project_imported_from_github.rb +++ b/qa/qa/resource/project_imported_from_github.rb @@ -12,7 +12,7 @@ module QA group.visit! Page::Group::Show.perform(&:go_to_new_project) - go_to_import_tab + go_to_import_page Page::Project::New.perform(&:click_github_link) Page::Project::Import::Github.perform do |import_page| @@ -21,7 +21,7 @@ module QA end end - def go_to_import_tab + def go_to_import_page Page::Project::New.perform(&:click_import_project) end end diff --git a/qa/qa/resource/project_snippet.rb b/qa/qa/resource/project_snippet.rb index c262499664e..9ab4612d117 100644 --- a/qa/qa/resource/project_snippet.rb +++ b/qa/qa/resource/project_snippet.rb @@ -33,12 +33,16 @@ module QA end def api_get_path - "/projects/#{project.id}/snippets/#{snippet_id}" + "/projects/#{project.id}/snippets/#{id}" end def api_post_path "/projects/#{project.id}/snippets" end + + def api_delete_path + "/projects/#{project.id}/snippets/#{id}" + end end end end diff --git a/qa/qa/resource/registry_repository.rb b/qa/qa/resource/registry_repository.rb new file mode 100644 index 00000000000..1e43d93a784 --- /dev/null +++ b/qa/qa/resource/registry_repository.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'securerandom' + +module QA + module Resource + class RegistryRepository < Base + attr_accessor :name, + :repository_id + + attribute :project do + Project.fabricate_via_api! do |resource| + resource.name = 'project-with-registry' + resource.description = 'Project with Registry' + end + end + + def initialize + @name = project.path_with_namespace + @repository_id = nil + end + + def fabricate! + end + + def fabricate_via_api! + resource_web_url(api_get) + rescue ResourceNotFoundError + super + end + + def remove_via_api! + registry_repositories = project.registry_repositories + if registry_repositories && !registry_repositories.empty? + this_registry_repository = registry_repositories.find { |registry_repository| registry_repository[:path] == name } + + @repository_id = this_registry_repository[:id] + + QA::Runtime::Logger.debug("Deleting registry '#{name}'") + super + end + end + + def api_delete_path + "/projects/#{project.id}/registry/repositories/#{@repository_id}" + end + + def api_get_path + "/projects/#{project.id}/registry/repositories" + end + end + end +end diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb index 6fdcb1cd29b..6423dc7a41c 100644 --- a/qa/qa/resource/snippet.rb +++ b/qa/qa/resource/snippet.rb @@ -3,7 +3,10 @@ module QA module Resource class Snippet < Base - attr_accessor :title, :description, :file_content, :visibility, :file_name, :snippet_id + attr_accessor :title, :description, :file_content, :visibility, :file_name + + attribute :id + attribute :http_url_to_repo def initialize @title = 'New snippet title' @@ -44,13 +47,17 @@ module QA end def api_get_path - "/snippets/#{snippet_id}" + "/snippets/#{id}" end def api_post_path '/snippets' end + def api_put_path + "/snippets/#{id}" + end + def api_post_body { title: title, @@ -60,12 +67,38 @@ module QA } end + def api_delete_path + "/snippets/#{id}" + 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 + + def has_file?(file_path) + response = get Runtime::API::Request.new(api_client, api_get_path).url + + raise ResourceNotFoundError, "Request returned (#{response.code}): `#{response}`." if response.code == HTTP_STATUS_NOT_FOUND + + file_output = parse_body(response)[:files] + file_output.any? { |file| file[:path] == file_path } + end + + def change_repository_storage(new_storage) + post_body = { destination_storage_name: new_storage } + response = post Runtime::API::Request.new(api_client, "/snippets/#{id}/repository_storage_moves").url, post_body + + unless response.code.between?(200, 300) + raise ResourceUpdateFailedError, "Could not change repository storage to #{new_storage}. Request returned (#{response.code}): `#{response}`." + end + + wait_until(sleep_interval: 1) { Runtime::API::RepositoryStorageMoves.has_status?(self, 'finished', new_storage) } + rescue Support::Repeater::RepeaterConditionExceededError + raise Runtime::API::RepositoryStorageMoves::RepositoryStorageMovesError, 'Timed out while waiting for the snippet repository storage move to finish' + end end end end diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb index f95a68918dc..d1a310c7c43 100644 --- a/qa/qa/resource/user.rb +++ b/qa/qa/resource/user.rb @@ -5,6 +5,8 @@ require 'securerandom' module QA module Resource class User < Base + InvalidUserError = Class.new(RuntimeError) + attr_reader :unique_id attr_writer :username, :password attr_accessor :admin, :provider, :extern_uid, :expect_fabrication_success @@ -21,6 +23,13 @@ module QA @expect_fabrication_success = true end + def self.default + Resource::User.new.tap do |user| + user.username = Runtime::User.ldap_user? ? Runtime::User.ldap_username : Runtime::User.username + user.password = Runtime::User.ldap_user? ? Runtime::User.ldap_password : Runtime::User.password + end + end + def admin? api_resource&.dig(:is_admin) || false end @@ -28,10 +37,12 @@ module QA def username @username || "qa-user-#{unique_id}" end + alias_method :ldap_username, :username def password @password || 'password' end + alias_method :ldap_password, :password def name @name ||= api_resource&.dig(:name) || "QA User #{unique_id}" @@ -138,8 +149,8 @@ module QA return {} unless extern_uid && provider { - extern_uid: extern_uid, - provider: provider + extern_uid: extern_uid, + provider: provider } end diff --git a/qa/qa/resource/wiki/group_page.rb b/qa/qa/resource/wiki/group_page.rb new file mode 100644 index 00000000000..27150ecf6c7 --- /dev/null +++ b/qa/qa/resource/wiki/group_page.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'securerandom' + +module QA + module Resource + module Wiki + class GroupPage < Base + attribute :title + attribute :content + attribute :slug + + attribute :group do + Group.fabricate_via_api! do |group| + group.path = "group-with-wiki-#{SecureRandom.hex(8)}" + end + end + + def initialize + @title = 'Home' + @content = 'This wiki page is created via API' + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + "#{group.web_url}/-/wikis/#{slug}" + end + + def api_get_path + "/groups/#{group.id}/wikis/#{slug}" + end + + def api_post_path + "/groups/#{group.id}/wikis" + end + + def api_post_body + { + id: group.id, + content: content, + title: title + } + end + end + end + end +end diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb index e4de033c309..4126ff9ff5a 100644 --- a/qa/qa/runtime/api/client.rb +++ b/qa/qa/runtime/api/client.rb @@ -23,22 +23,30 @@ module QA # unless a specific user has been passed @user.nil? ? Runtime::Env.personal_access_token ||= create_personal_access_token : create_personal_access_token end + + if @user&.admin? + Runtime::Env.admin_personal_access_token = @personal_access_token + end + + @personal_access_token end def self.as_admin - if Runtime::Env.admin_personal_access_token - Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) - else - user = Resource::User.fabricate_via_api! do |user| - user.username = Runtime::User.admin_username - user.password = Runtime::User.admin_password + @admin_client ||= begin + if Runtime::Env.admin_personal_access_token + Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) + else + user = Resource::User.fabricate_via_api! do |user| + user.username = Runtime::User.admin_username + user.password = Runtime::User.admin_password + end + + unless user.admin? + raise AuthorizationError, "User '#{user.username}' is not an administrator." + end + + Runtime::API::Client.new(:gitlab, user: user) end - - unless user.admin? - raise AuthorizationError, "User '#{user.username}' is not an administrator." - end - - Runtime::API::Client.new(:gitlab, user: user) end end @@ -67,9 +75,9 @@ module QA Page::Main::Menu.perform(&:sign_out) if @is_new_session && signed_in_initially - Flow::Login.sign_in_unless_signed_in(as: @user) - - token = Resource::PersonalAccessToken.fabricate!.access_token + token = Resource::PersonalAccessToken.fabricate! do |pat| + pat.user = user + end.token # If this is a new session, that tests that follow could fail if they # try to sign in without starting a new session. diff --git a/qa/qa/runtime/api/repository_storage_moves.rb b/qa/qa/runtime/api/repository_storage_moves.rb index d0211d3f66d..5630a9c02c5 100644 --- a/qa/qa/runtime/api/repository_storage_moves.rb +++ b/qa/qa/runtime/api/repository_storage_moves.rb @@ -9,9 +9,9 @@ module QA RepositoryStorageMovesError = Class.new(RuntimeError) - def has_status?(project, status, destination_storage = Env.additional_repository_storage) - find_any do |move| - next unless move[:project][:path_with_namespace] == project.path_with_namespace + def has_status?(resource, status, destination_storage = Env.additional_repository_storage) + find_any(resource) do |move| + next unless resource_equals?(resource, move) QA::Runtime::Logger.debug("Move data: #{move}") @@ -20,16 +20,28 @@ module QA end end - def find_any + def find_any(resource) Logger.debug('Getting repository storage moves') Support::Waiter.wait_until do - with_paginated_response_body(Request.new(api_client, '/project_repository_storage_moves', per_page: '100').url) do |page| + with_paginated_response_body(Request.new(api_client, "/#{resource_name(resource)}_repository_storage_moves", per_page: '100').url) do |page| break true if page.any? { |item| yield item } end end end + def resource_equals?(resource, move) + if resource.class.name.include?('Snippet') + move[:snippet][:id] == resource.id + else + move[:project][:path_with_namespace] == resource.path_with_namespace + end + end + + def resource_name(resource) + resource.class.name.split('::').last.downcase + end + private def api_client diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 6c4139da83f..7aa45204513 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 + attr_writer :personal_access_token, :admin_personal_access_token ENV_VARIABLES = Gitlab::QA::Runtime::Env::ENV_VARIABLES @@ -78,18 +78,6 @@ module QA ENV['QA_PRAEFECT_REPOSITORY_STORAGE'] end - def admin_password - ENV['GITLAB_ADMIN_PASSWORD'] - end - - def admin_username - ENV['GITLAB_ADMIN_USERNAME'] - end - - def admin_personal_access_token - ENV['GITLAB_QA_ADMIN_ACCESS_TOKEN'] - end - def ci_job_url ENV['CI_JOB_URL'] end @@ -140,6 +128,18 @@ module QA enabled?(ENV['SIGNUP_DISABLED'], default: false) end + def admin_password + ENV['GITLAB_ADMIN_PASSWORD'] + end + + def admin_username + ENV['GITLAB_ADMIN_USERNAME'] + end + + def admin_personal_access_token + @admin_personal_access_token ||= ENV['GITLAB_QA_ADMIN_ACCESS_TOKEN'] + end + # specifies token that can be used for the api def personal_access_token @personal_access_token ||= ENV['GITLAB_QA_ACCESS_TOKEN'] diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb index ab4f28c292f..119013175c0 100644 --- a/qa/qa/service/praefect_manager.rb +++ b/qa/qa/service/praefect_manager.rb @@ -353,15 +353,47 @@ module QA Support::Waiter.wait_until(sleep_interval: 1) { replication_queue_incomplete_count == 0 && replicated?(project_id) } end + def replication_pending? + result = [] + shell sql_to_docker_exec_cmd( + <<~SQL + select job from replication_queue + where state = 'ready' + and job ->> 'change' = 'update' + and job ->> 'source_node_storage' = '#{current_primary_node}' + and job ->> 'target_node_storage' = '#{@primary_node}'; + SQL + ) do |line| + result << line + end + + # The result looks like: + # + # job + # ----------- + # {"change": "update", "params": null, "relative_path": "@hashed/4b/22/4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a.git", "virtual_storage": "default", "source_node_storage": "gitaly3", "target_node_storage": "gitaly1"} + # (1 row) + # <blank row> + # + # Therefore when replication is pending there is at least 1 row of data plus 4 rows of metadata/layout + + result.size >= 5 + end + private def current_primary_node - shell dataloss_command do |line| - QA::Runtime::Logger.debug(line.chomp) - - match = line.match(/Primary: (.*)/) - break match[1] if match + result = [] + shell sql_to_docker_exec_cmd("select node_name from shard_primaries where shard_name = '#{@virtual_storage}';") do |line| + result << line end + # The result looks like: + # node_name + # ----------- + # gitaly1 + # (1 row) + + result[2].strip end def dataloss_command diff --git a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb index 89bf92cd3af..c90f8546f91 100644 --- a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb +++ b/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Create' do context 'Gitaly' do - describe 'Backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/238186', type: :investigating } do + describe 'Backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/322647', type: :flaky } do let(:praefect_manager) { Service::PraefectManager.new } let(:project) do Resource::Project.fabricate! do |project| @@ -50,18 +50,17 @@ module QA push.file_content = 'new file' end + # Confirm that the commit is waiting to be replicated + expect(praefect_manager).to be_replication_pending + # 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 + praefect_manager.wait_for_health_check_all_nodes - # Reconcile nodes to recover from dataloss - praefect_manager.reconcile_nodes + # Wait for automatic replication praefect_manager.wait_for_replication(project.id) - # Confirm that both commits are available after reconciliation + # Confirm that both commits are available expect(project.commits.map { |commit| commit[:message].chomp }) .to include("Initial commit").and include("pushed after failover") end diff --git a/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb index d6bd668fa8a..bf2ecfdb513 100644 --- a/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb +++ b/qa/qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb @@ -38,7 +38,11 @@ module QA end.merge_via_api! expect(merge_request[:state]).to eq('merged') - expect(project).not_to have_branch(branch) + + # Retry in case the branch deletion takes more time to finish + QA::Support::Retrier.retry_on_exception(max_attempts: 5, sleep_interval: 5) do + expect(project).not_to have_branch(branch) + end end end end diff --git a/qa/qa/specs/features/api/3_create/snippet/snippet_repository_storage_move_spec.rb b/qa/qa/specs/features/api/3_create/snippet/snippet_repository_storage_move_spec.rb new file mode 100644 index 00000000000..4872acd1004 --- /dev/null +++ b/qa/qa/specs/features/api/3_create/snippet/snippet_repository_storage_move_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Snippet repository storage', :requires_admin, :orchestrated, :repository_storage do + let(:source_storage) { { type: :gitaly, name: 'default' } } + let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.additional_repository_storage } } + + let(:snippet) do + Resource::Snippet.fabricate_via_api! do |snippet| + snippet.title = 'Snippet to move storage of' + snippet.file_name = 'original_file' + snippet.file_content = 'Original file content' + snippet.api_client = Runtime::API::Client.as_admin + end + end + + praefect_manager = Service::PraefectManager.new + + before do + praefect_manager.gitlab = 'gitlab' + end + + it 'moves snippet repository from one Gitaly storage to another', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1700' do + expect(snippet).to have_file('original_file') + expect { snippet.change_repository_storage(destination_storage[:name]) }.not_to raise_error + expect { praefect_manager.verify_storage_move(source_storage, destination_storage) }.not_to raise_error + + # verifies you can push commits to the moved snippet + Resource::Repository::Push.fabricate! do |push| + push.repository_http_uri = snippet.http_url_to_repo + push.file_name = 'new_file' + push.file_content = 'new file content' + push.commit_message = 'Adding a new snippet file' + push.new_branch = false + end + + aggregate_failures do + expect(snippet).to have_file('original_file') + expect(snippet).to have_file('new_file') + end + end + end + end +end diff --git a/qa/qa/specs/features/api/4_verify/.gitkeep b/qa/qa/specs/features/api/4_verify/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/specs/features/api/4_verify/.gitkeep diff --git a/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb b/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb deleted file mode 100644 index 1d1b765bb9f..00000000000 --- a/qa/qa/specs/features/api/4_verify/pipeline_deletion_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Verify' do - include Support::Api - - let(:api_client) { Runtime::API::Client.new(:gitlab) } - - describe 'Pipeline', :runner do - let(:project) do - Resource::Project.fabricate_via_api! do |project| - project.name = 'project-with-pipeline' - end - end - - let!(:runner) do - Resource::Runner.fabricate! do |runner| - runner.project = project - runner.name = project.name - runner.tags = [project.name] - 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 - job1: - tags: - - #{project.name} - script: echo 'OK' - YAML - } - ] - ) - end - end - - let!(:pipeline_id) do - pipeline_create_request = Runtime::API::Request.new(api_client, "/projects/#{project.id}/pipeline?ref=#{project.default_branch}") - JSON.parse(post(pipeline_create_request.url, nil))['id'] - end - - let(:pipeline_data_request) { Runtime::API::Request.new(api_client, "/projects/#{project.id}/pipelines/#{pipeline_id}") } - - before do - Support::Waiter.wait_until(sleep_interval: 3) { !pipeline.empty? && pipeline['status'] != 'pending' } - end - - after do - runner.remove_via_api! - end - - context 'when deleted via API' do - it 'is not found', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/931' do - delete(pipeline_data_request.url) - - deleted_pipeline = nil - Support::Waiter.wait_until(sleep_interval: 3) do - deleted_pipeline = pipeline - !pipeline.empty? - end - - raise "Pipeline response does not have a 'message' key: #{deleted_pipeline}" unless deleted_pipeline&.key?('message') - - expect(deleted_pipeline['message'].downcase).to have_content('404 not found') - end - end - - private - - def pipeline - JSON.parse(get(pipeline_data_request.url)) - end - end - end -end diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb index 5f38907f89f..7ab5ffdf014 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb @@ -44,8 +44,7 @@ module QA end it 'user transfers a project between groups', - testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/406', - quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/283925', type: :investigating, only: :production } do + testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1703' do # Retry is needed here as the target group is not avaliable for transfer right away. QA::Support::Retrier.retry_on_exception(reload_page: page) do Page::File::Show.perform(&:go_to_general_settings) 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 e38a9f47bd6..8e03444b113 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 @@ -29,6 +29,7 @@ module QA end before do + Runtime::Feature.enable(:invite_members_group_modal, group: group) group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER) end 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 2fc3ff2f18f..a8650d633c8 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 @@ -31,6 +31,7 @@ module QA let(:two_fa_expected_text) { /The group settings for.*require you to enable Two-Factor Authentication for your account.*You need to do this before/ } before do + Runtime::Feature.enable(:invite_members_group_modal, group: group) group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER) 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 ffc2290b644..4141060b6cb 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 @@ -23,6 +23,7 @@ module QA @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 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 85cfefd6c5c..bf59dcfcc4c 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 @@ -4,11 +4,7 @@ module QA RSpec.describe 'Manage', :requires_admin do describe 'Add project member' do before do - Runtime::Feature.enable('vue_project_members_list') - end - - after do - Runtime::Feature.disable('vue_project_members_list') + Runtime::Feature.enable(:invite_members_group_modal) end it 'user adds project member', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/482' do @@ -16,9 +12,11 @@ module QA user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) - Resource::Project.fabricate_via_api! do |project| + project = Resource::Project.fabricate_via_api! do |project| project.name = 'add-member-project' - end.visit! + end + + project.visit! Page::Project::Menu.perform(&:click_members) Page::Project::Members.perform do |members| diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb index f2822e3d368..95092fe6ba6 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb @@ -47,7 +47,7 @@ module QA end end - context 'when logged in as a new admin', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/463' do + context 'when logged in as a new admin', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1727' do it_behaves_like 'loads all images' do let(:new_user) { @new_admin } 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 d54ce0ac0fc..c8c483fc2aa 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 @@ -21,6 +21,7 @@ module QA end before do + Runtime::Feature.enable(:invite_members_group_modal, group: group) group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb index e40dde64675..714c4a2da67 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/protected_tags_spec.rb @@ -2,7 +2,8 @@ module QA RSpec.describe 'Manage' do - describe 'Repository tags' do + # TODO: Remove :requires_admin meta when the `Runtime::Feature.enable` method call is removed + describe 'Repository tags', :requires_admin do let(:project) do Resource::Project.fabricate_via_api! do |project| project.name = 'project-for-tags' @@ -10,6 +11,10 @@ module QA end end + before do + Runtime::Feature.enable(:invite_members_group_modal, project: project) + end + let(:developer_user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) } let(:maintainer_user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) } let(:tag_name) { 'v0.0.1' } diff --git a/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb index f623efe5a3a..d352996f419 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb @@ -16,14 +16,10 @@ module QA end before do - Runtime::Feature.enable('vue_project_members_list', project: project) + Runtime::Feature.enable(:invite_members_group_modal) Flow::Login.sign_in end - after do - Runtime::Feature.disable('vue_project_members_list', project: project) - end - it 'is received by a user for project invitation', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/676' do project.visit! diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb index a7f862e8911..9129584d110 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb @@ -18,6 +18,8 @@ module QA describe 'check xss occurence in @mentions in issues', :requires_admin do before do + Runtime::Feature.enable(:invite_members_group_modal) + Flow::Login.sign_in project.add_member(user) diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb index cc815fd0e67..45222d2ec7e 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module QA - RSpec.describe 'Plan', :smoke, :reliable do + # TODO: Remove :requires_admin meta when the `Runtime::Feature.enable` method call is removed + RSpec.describe 'Plan', :smoke, :reliable, :requires_admin do describe 'mention' do let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) } let(:project) do @@ -13,6 +14,7 @@ module QA before do Flow::Login.sign_in + Runtime::Feature.enable(:invite_members_group_modal, project: project) project.add_member(user) 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 index 11f93d6a97e..2654531bc2c 100644 --- 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 @@ -14,6 +14,7 @@ module QA before do Runtime::Feature.enable('real_time_issue_sidebar', project: project) Runtime::Feature.enable('broadcast_issue_updates', project: project) + Runtime::Feature.enable(:invite_members_group_modal, project: project) Flow::Login.sign_in diff --git a/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb b/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb index 7090427e5a4..43cf701acdd 100644 --- a/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/design_management/archive_design_content_spec.rb @@ -15,7 +15,7 @@ module QA let(:third_design) do Resource::Design.fabricate! do |design| design.issue = second_design.issue - design.filename = 'tanuki.jpg' + design.filename = 'testfile.png' end end diff --git a/qa/qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb b/qa/qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb index 135063b6644..9fbb0d69de1 100644 --- a/qa/qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/design_management/modify_design_content_spec.rb @@ -5,7 +5,7 @@ module QA context 'Design Management' do let(:design) do Resource::Design.fabricate! do |design| - design.filename = 'tanuki.jpg' + design.filename = 'testfile.png' end end diff --git a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb b/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb index 0fec7bc9e9d..734ff160937 100644 --- a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb @@ -107,7 +107,7 @@ module QA def fabricate_personal_access_token login_to_gitlab - token = Resource::PersonalAccessToken.fabricate!.access_token + token = Resource::PersonalAccessToken.fabricate!.token Page::Main::Menu.perform(&:sign_out) token end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb index d2ba97400e6..8d12042bd85 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb @@ -9,7 +9,7 @@ module QA end end - it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/928' do + it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1701' do Flow::Login.while_signed_in do merge_request.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb new file mode 100644 index 00000000000..2ddc59acd5c --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create', :runner do + describe 'Merge requests' do + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'merge-when-pipeline-succeeds' + project.initialize_with_readme = true + end + end + + let!(:runner) do + Resource::Runner.fabricate! do |runner| + runner.project = project + runner.name = "runner-for-#{project.name}" + runner.tags = ["runner-for-#{project.name}"] + end + end + + before 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: <<~EOF + test: + tags: ["runner-for-#{project.name}"] + script: sleep 5 + only: + - merge_requests + EOF + } + ] + ) + end + + Flow::Login.sign_in + end + + after do + runner&.remove_via_api! + project&.remove_via_api! + end + + it 'merges when pipeline succeeds', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1684' do + branch_name = "merge-request-test-#{SecureRandom.hex(8)}" + + # Create a branch that will be merged into the default branch + Resource::Repository::ProjectPush.fabricate! do |project_push| + project_push.project = project + project_push.new_branch = true + project_push.branch_name = branch_name + project_push.file_name = "file-#{SecureRandom.hex(8)}.txt" + end + + # Create a merge request to merge the branch we just created + merge_request = Resource::MergeRequest.fabricate_via_api! do |merge_request| + merge_request.project = project + merge_request.source_branch = branch_name + merge_request.no_preparation = true + end + + merge_request.visit! + + Page::MergeRequest::Show.perform do |mr| + mr.merge_when_pipeline_succeeds! + + expect(mr.merge_request_status).to match(/to be merged automatically when the pipeline succeeds/) + + Support::Waiter.wait_until(sleep_interval: 5) do + merge_request = merge_request.reload! + merge_request.state == 'merged' + end + + expect(mr.merged?).to be_truthy, "Expected content 'The changes were merged' but it did not appear." + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb index 3414584bae3..34669eb3815 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/30226', type: :bug } do + RSpec.describe 'Create', quarantine: { only: { subdomain: :staging }, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/323990', type: :flaky } do describe 'Merge request rebasing' do it 'user rebases source branch of merge request', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1274' do Flow::Login.sign_in @@ -34,7 +34,7 @@ module QA Page::MergeRequest::Show.perform do |merge_request| expect(merge_request).to have_content('Needs rebasing') - expect(merge_request).not_to be_fast_forward_possible + expect(merge_request).to be_fast_forward_not_possible expect(merge_request).not_to have_merge_button merge_request.rebase! diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb index 9edde7ac12f..fb9e42a6960 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Git clone over HTTP', :ldap_no_tls do + describe 'Git clone over HTTP' do let(:project) do Resource::Project.fabricate_via_api! do |scenario| scenario.name = 'project-with-code' diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb index 7a727ffb291..0f6edca654b 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb @@ -19,6 +19,7 @@ module QA end before do + Runtime::Feature.enable(:invite_members_group_modal, project: parent_project) parent_project.add_member(user) end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb index 35ec2135491..2c0fb5ea290 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb @@ -2,11 +2,11 @@ module QA RSpec.describe 'Create' do - describe 'Git push over HTTP', :ldap_no_tls, :smoke do + describe 'Git push over HTTP', :smoke do it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1278' do Flow::Login.sign_in - access_token = Resource::PersonalAccessToken.fabricate!.access_token + access_token = Resource::PersonalAccessToken.fabricate!.token user = Resource::User.new.tap do |user| user.username = Runtime::User.username diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb index 222eb3771ad..1423e3c45ce 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb @@ -17,7 +17,7 @@ module QA p.initialize_with_readme = true end - @api_client = Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) + @api_client = Runtime::API::Client.as_admin end after(:context) do diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb index cf14017b7f1..861efa8b45a 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb @@ -2,8 +2,8 @@ module QA RSpec.describe 'Create' do - describe 'Git push over HTTP', :ldap_no_tls do - it 'user pushes code to the repository', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/426' do + describe 'Git push over HTTP' do + it 'user pushes code to the repository', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1702' do Flow::Login.sign_in Resource::Repository::ProjectPush.fabricate! do |push| diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb index c56e6d1267c..9eeb762e548 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb @@ -41,8 +41,7 @@ module QA end end - it 'pushes multiple branches and tags together', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1679 - ' do + it 'pushes multiple branches and tags together', :smoke, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1679' do branches = [] tags = [] Git::Repository.perform do |repository| diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb index 2b249f779d9..ce7fdf379a4 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Protected branch support', :ldap_no_tls do + describe 'Protected branch support' do let(:branch_name) { 'protected-branch' } let(:commit_message) { 'Protected push commit message' } let(:project) do diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb index a867d9cb973..6ab50ba56f2 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb @@ -23,6 +23,11 @@ module QA Flow::Login.sign_in end + after do + personal_snippet&.remove_via_api! + project_snippet&.remove_via_api! + end + shared_examples 'comments on snippets' do |snippet_type| it "adds, edits, and deletes a comment on a #{snippet_type}" do send(snippet_type) 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 index 604c98ca21e..72d83eadde9 100644 --- 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 @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Multiple file snippet', quarantine: { only: { pipeline: :master }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } 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' @@ -23,6 +23,11 @@ module QA Flow::Login.sign_in end + after do + personal_snippet&.remove_via_api! + project_snippet&.remove_via_api! + 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! 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 2940b2067a1..d44f5bc9e2e 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 @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Version control for personal snippets', quarantine: { only: { pipeline: :master }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } do + describe 'Version control for personal snippets' do let(:new_file) { 'new_snippet_file' } let(:changed_content) { 'changes' } let(:commit_message) { 'Changes to snippets' } @@ -66,6 +66,8 @@ module QA expect(repository.commits.first).to include('Update snippet') expect(repository.file_content(new_file)).to include("#{added_content}#{changed_content}") end + + snippet.remove_via_api! end it 'clones, pushes, and pulls a snippet over SSH, deletes via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/825' do 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 b91424d5b65..b67e0e54aa0 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 @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Version control for project snippets', quarantine: { only: { pipeline: :master }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } do + describe 'Version control for project snippets' do let(:new_file) { 'new_snippet_file' } let(:changed_content) { 'changes' } let(:commit_message) { 'Changes to snippets' } @@ -40,7 +40,7 @@ module QA ssh_key.remove_via_api! end - it 'clones, pushes, and pulls a project snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/833' do + it 'clones, pushes, and pulls a project snippet over HTTP, edits via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1725' do Resource::Repository::Push.fabricate! do |push| push.repository_http_uri = repository_uri_http push.file_name = new_file @@ -67,6 +67,8 @@ module QA expect(repository.commits.first).to include 'Update snippet' expect(repository.file_content(new_file)).to include "#{added_content}#{changed_content}" end + + snippet.remove_via_api! end it 'clones, pushes, and pulls a project snippet over SSH, deletes via UI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/832' do diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb index 1670ba56064..29ddbb22a01 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb @@ -54,6 +54,11 @@ module QA Flow::Login.sign_in end + after do + personal_snippet&.remove_via_api! + project_snippet&.remove_via_api! + end + shared_examples 'copying snippet file contents' do |snippet_type| it "copies file contents of a multi-file #{snippet_type} to a comment and verifies them" do send(snippet_type).visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb index b74f27389a0..ddbc98c4a1a 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb @@ -3,7 +3,7 @@ module QA RSpec.describe 'Create', :smoke do describe 'Personal snippet creation' do - it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/840' do + it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1704' do Flow::Login.sign_in Page::Main::Menu.perform do |menu| 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 index 5c0983dabb6..014c0ca4d48 100644 --- 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 @@ -2,7 +2,7 @@ module QA RSpec.describe 'Create' do - describe 'Multiple file snippet', quarantine: { only: { pipeline: :master }, issue: 'https://gitlab.com/gitlab-org/gitaly/-/issues/3143', type: :bug } 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' @@ -31,6 +31,11 @@ module QA Flow::Login.sign_in end + after do + personal_snippet&.remove_via_api! + project_snippet&.remove_via_api! + 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! 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 751424222ba..3973e0aacad 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 @@ -16,6 +16,10 @@ module QA Flow::Login.sign_in end + after do + snippet&.remove_via_api! + end + 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.visit! diff --git a/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb new file mode 100644 index 00000000000..5e663d87ae3 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Package', :orchestrated do + describe 'Container Registry', only: { subdomain: :staging } do + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'project-with-registry' + project.template_name = 'express' + end + end + + let(:registry_repository) do + Resource::RegistryRepository.fabricate! do |repository| + repository.name = "#{project.path_with_namespace}" + repository.project = project + end + end + + let!(:gitlab_ci_yaml) do + <<~YAML + build: + image: docker:19.03.12 + stage: build + services: + - docker:19.03.12-dind + variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -t $IMAGE_TAG . + - docker push $IMAGE_TAG + YAML + end + + after do + registry_repository&.remove_via_api! + end + + it 'pushes project image to the container registry and deletes tag', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1699' do + Flow::Login.sign_in + project.visit! + + 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 + }]) + end + + Flow::Pipeline.visit_latest_pipeline + + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('build') + end + + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end + + Page::Project::Menu.perform(&:go_to_container_registry) + + Page::Project::Registry::Show.perform do |registry| + expect(registry).to have_registry_repository(registry_repository.name) + + registry.click_on_image(registry_repository.name) + expect(registry).to have_tag('master') + + registry.click_delete + expect(registry).not_to have_tag('master') + 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 index 6ab4a957c57..b5b050a5dfe 100644 --- 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 @@ -13,7 +13,7 @@ module QA Flow::Login.sign_in end - Resource::PersonalAccessToken.fabricate!.access_token + Resource::PersonalAccessToken.fabricate!.token end let(:project) do diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb index 605248e33f7..2f508d00790 100644 --- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb @@ -1,19 +1,21 @@ # frozen_string_literal: true +require 'securerandom' + module QA RSpec.describe 'Package', :orchestrated, :packages do describe 'Maven Repository' do include Runtime::Fixtures let(:group_id) { 'com.gitlab.qa' } - let(:artifact_id) { 'maven' } + let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" } 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 + Resource::PersonalAccessToken.fabricate!.token end let(:project) do @@ -22,12 +24,19 @@ module QA end end + let(:another_project) do + Resource::Project.fabricate_via_api! do |another_project| + another_project.name = 'another-maven-package-project' + another_project.group = project.group + 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.tags = ["runner-for-#{project.group.name}"] runner.executor = :docker - runner.project = project + runner.token = project.group.sandbox.runners_token end end @@ -90,6 +99,28 @@ module QA } end + let(:gitlab_ci_yaml) do + { + file_path: '.gitlab-ci.yml', + content: + <<~YAML + deploy: + image: maven:3.6-jdk-11 + script: + - 'mvn deploy -s settings.xml' + - "mvn dependency:get -Dartifact=#{group_id}:#{artifact_id}:1.0" + only: + - "#{project.default_branch}" + tags: + - "runner-for-#{project.group.name}" + YAML + } + end + + after do + runner.remove_via_api! + end + it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943' do # Use a maven docker container to deploy the package with_fixtures([pom_xml, settings_xml]) do |dir| @@ -122,23 +153,9 @@ module QA commit.project = project commit.commit_message = 'Add .gitlab-ci.yml' commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: - <<~YAML - deploy: - image: maven:3.6-jdk-11 - script: - - 'mvn deploy -s settings.xml' - - "mvn dependency:get -Dartifact=#{group_id}:#{artifact_id}:1.0" - only: - - "#{project.default_branch}" - tags: - - "runner-for-#{project.name}" - YAML - }, - settings_xml, - pom_xml + gitlab_ci_yaml, + settings_xml, + pom_xml ]) end @@ -153,6 +170,96 @@ module QA expect(job).to be_successful(timeout: 800) end end + + context 'when "allow duplicate" setting is disabled' do + before do + Flow::Login.sign_in + + project.group.visit! + + Page::Group::Menu.perform(&:go_to_package_settings) + Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled) + end + + it 'prevents users from publishing duplicate Maven packages at the group level', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1717' do + with_fixtures([pom_xml, settings_xml]) do |dir| + Service::DockerRun::Maven.new(dir).publish! + end + + project.visit! + Page::Project::Menu.perform(&:click_packages_link) + + Page::Project::Packages::Index.perform do |index| + expect(index).to have_package(package_name) + end + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = another_project + commit.commit_message = 'Add .gitlab-ci.yml' + commit.add_files([ + gitlab_ci_yaml, + settings_xml, + pom_xml + ]) + end + + another_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).not_to be_successful(timeout: 800) + end + end + end + + context 'when "allow duplicate" setting is enabled' do + before do + Flow::Login.sign_in + + project.group.visit! + + Page::Group::Menu.perform(&:go_to_package_settings) + Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled) + end + + it 'allows users to publish duplicate Maven packages at the group level', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1722' do + with_fixtures([pom_xml, settings_xml]) do |dir| + Service::DockerRun::Maven.new(dir).publish! + end + + project.visit! + Page::Project::Menu.perform(&:click_packages_link) + + Page::Project::Packages::Index.perform do |index| + expect(index).to have_package(package_name) + end + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = another_project + commit.commit_message = 'Add .gitlab-ci.yml' + commit.add_files([ + gitlab_ci_yaml, + settings_xml, + pom_xml + ]) + end + + another_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 + end + end end end end diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb index fa88ace1556..97df8fedf87 100644 --- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Package', :orchestrated, :packages do - describe 'NPM registry' do + describe 'npm registry' do include Runtime::Fixtures let(:registry_scope) { project.group.sandbox.path } @@ -12,7 +12,7 @@ module QA Flow::Login.sign_in end - Resource::PersonalAccessToken.fabricate!.access_token + Resource::PersonalAccessToken.fabricate!.token end let(:project) do @@ -31,7 +31,7 @@ module QA { "name": "#{package_name}", "version": "1.0.0", - "description": "Example package for GitLab NPM registry", + "description": "Example package for GitLab npm registry", "publishConfig": { "@#{registry_scope}:registry": "#{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/npm/" } 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 index 8c3f0930207..f143bc52095 100644 --- 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 @@ -1,11 +1,13 @@ # frozen_string_literal: true +require 'securerandom' + module QA RSpec.describe 'Package', :orchestrated, :packages do describe 'NuGet Repository' do include Runtime::Fixtures - let(:package_name) { 'dotnetcore' } + let(:package_name) { "dotnetcore-#{SecureRandom.hex(8)}" } let(:project) do Resource::Project.fabricate_via_api! do |project| project.name = 'nuget-package-project' @@ -54,7 +56,7 @@ module QA { file_path: '.gitlab-ci.yml', content: <<~YAML - image: mcr.microsoft.com/dotnet/core/sdk:3.1 + image: mcr.microsoft.com/dotnet/sdk:5.0 stages: - deploy @@ -64,7 +66,7 @@ module QA script: - dotnet restore -p:Configuration=Release - dotnet build -c Release - - dotnet pack -c Release + - dotnet pack -c Release -p:PackageID=#{package_name} - 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: @@ -102,7 +104,7 @@ module QA <PropertyGroup> <OutputType>Exe</OutputType> - <TargetFramework>netcoreapp3.1</TargetFramework> + <TargetFramework>net5.0</TargetFramework> </PropertyGroup> </Project> @@ -115,7 +117,7 @@ module QA { file_path: '.gitlab-ci.yml', content: <<~YAML - image: mcr.microsoft.com/dotnet/core/sdk:3.1 + image: mcr.microsoft.com/dotnet/sdk:5.0 stages: - install 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 171a4776eaf..e2cf5c5b195 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 @@ -23,9 +23,7 @@ module QA cluster&.remove! end - it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/702' do - skip('Test requires tunnel: see https://gitlab.com/gitlab-org/gitlab/-/issues/251090') - + it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1715' do Flow::Login.sign_in # Set an application secret CI variable (prefixed with K8S_SECRET_) diff --git a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb index 8dca0d6698e..302fec55c53 100644 --- a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb +++ b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb @@ -20,7 +20,7 @@ module QA end end - it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247467', type: :investigating } do + it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478', quarantine: { only: { pipeline: :master }, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/323051', type: :bug } do # Issue pages always make AJAX requests Resource::Issue.fabricate_via_browser_ui! do |issue| issue.title = 'Performance bar test' diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb index 5fc36b68e5c..de9da3171b0 100644 --- a/qa/qa/support/api.rb +++ b/qa/qa/support/api.rb @@ -9,6 +9,7 @@ module QA HTTP_STATUS_CREATED = 201 HTTP_STATUS_NO_CONTENT = 204 HTTP_STATUS_ACCEPTED = 202 + HTTP_STATUS_NOT_FOUND = 404 HTTP_STATUS_SERVER_ERROR = 500 def post(url, payload, args = {}) diff --git a/qa/qa/support/ssh.rb b/qa/qa/support/ssh.rb index a5e8e96cb6c..1b53244d1e4 100644 --- a/qa/qa/support/ssh.rb +++ b/qa/qa/support/ssh.rb @@ -51,11 +51,21 @@ module QA private def uri_port - uri.port && (uri.port != 80) ? uri.port : nil + use_typical_params? ? nil : uri.port end def git_user - QA::Runtime::Env.running_in_ci? || [443, 80].include?(uri.port) ? 'git' : Etc.getlogin + QA::Runtime::Env.running_in_ci? || use_typical_params? ? 'git' : Etc.getlogin + end + + # Checks if typical parameters should be used. That means the SSH port will not be + # needed because it's port 22, and the git user is named 'git'. We assume that + # typical parameters should be used if the host URI includes a typical HTTP(S) + # port (80 or 443) + # + # @return [Boolean] whether typical SSH port and git user parameters should be used + def use_typical_params? + [443, 80].include?(uri.port) end end end diff --git a/qa/qa/tools/initialize_gitlab_auth.rb b/qa/qa/tools/initialize_gitlab_auth.rb index b06ddcab040..3ead8fc9bd4 100644 --- a/qa/qa/tools/initialize_gitlab_auth.rb +++ b/qa/qa/tools/initialize_gitlab_auth.rb @@ -23,7 +23,7 @@ module QA Flow::Login.sign_in puts "Creating an API scoped access token for the root user..." - puts "Token: #{Resource::PersonalAccessToken.fabricate!.access_token}" + puts "Token: #{Resource::PersonalAccessToken.fabricate!.token}" end end end diff --git a/qa/spec/runtime/api/client_spec.rb b/qa/spec/runtime/api/client_spec.rb index dd139fda980..36ee563de39 100644 --- a/qa/spec/runtime/api/client_spec.rb +++ b/qa/spec/runtime/api/client_spec.rb @@ -60,7 +60,7 @@ RSpec.describe QA::Runtime::API::Client do end it 'returns a created token' do - client = described_class.new(user: { username: 'foo' }) + client = described_class.new(user: Struct.new(:username, :admin?).new('foo', false)) expect(client).to receive(:create_personal_access_token).and_return('created_token') diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index 3396ae6f0b8..5a98721466f 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -232,6 +232,7 @@ RSpec.describe QA::Runtime::Env do describe '.require_admin_access_token!' do it 'raises ArgumentError if GITLAB_QA_ADMIN_ACCESS_TOKEN is not specified' do + described_class.instance_variable_set(:@admin_personal_access_token, nil) stub_env('GITLAB_QA_ADMIN_ACCESS_TOKEN', nil) expect { described_class.require_admin_access_token! }.to raise_error(ArgumentError) diff --git a/qa/spec/specs/helpers/quarantine_spec.rb b/qa/spec/specs/helpers/quarantine_spec.rb index 80fd65faeed..694c320ce3d 100644 --- a/qa/spec/specs/helpers/quarantine_spec.rb +++ b/qa/spec/specs/helpers/quarantine_spec.rb @@ -15,7 +15,7 @@ end # expanding into the global state # See: https://github.com/rspec/rspec-core/issues/2603 def describe_successfully(*args, &describe_body) - example_group = RSpec.describe(*args, &describe_body) + example_group = RSpec.describe(*args, &describe_body) ran_successfully = example_group.run RaiseOnFailuresReporter expect(ran_successfully).to eq true example_group @@ -156,28 +156,51 @@ RSpec.describe QA::Specs::Helpers::Quarantine do described_class.configure_rspec end - it 'is skipped when set on contexts or descriptions' do - group = describe_successfully 'Quarantined in staging', quarantine: { only: { subdomain: :staging } } do - it('runs in staging') {} + context 'no pipeline specified' do + it 'is skipped when set on contexts or descriptions' do + group = describe_successfully 'Quarantined in staging', quarantine: { only: { subdomain: :staging } } do + it('runs in staging') {} + end + + expect(group.examples.first.execution_result.status).to eq(:pending) + expect(group.examples.first.execution_result.pending_message) + .to eq('In quarantine') end - expect(group.examples.first.execution_result.status).to eq(:pending) - expect(group.examples.first.execution_result.pending_message) - .to eq('In quarantine') + it 'is skipped only in staging' do + group = describe_successfully do + it('skipped in staging', quarantine: { only: { subdomain: :staging } }) {} + it('runs in staging', quarantine: { only: :production }) {} + it('skipped in staging also', quarantine: { only: { subdomain: %i[release staging] } }) {} + it('runs in any env') {} + end + + expect(group.examples[0].execution_result.status).to eq(:pending) + expect(group.examples[1].execution_result.status).to eq(:passed) + expect(group.examples[2].execution_result.status).to eq(:pending) + expect(group.examples[3].execution_result.status).to eq(:passed) + end end - it 'is skipped only in staging' do - group = describe_successfully do - it('skipped in staging', quarantine: { only: { subdomain: :staging } }) {} - it('runs in staging', quarantine: { only: :production }) {} - it('skipped in staging also', quarantine: { only: { subdomain: %i[release staging] } }) {} - it('runs in any env') {} + context 'multiple pipelines specified' do + shared_examples 'skipped in project' do |project| + before do + stub_env('CI_PROJECT_NAME', project) + described_class.configure_rspec + end + + it "is skipped in #{project}" do + group = describe_successfully do + it('does not run in specified projects', quarantine: { only: { pipeline: [:staging, :canary, :production] } }) {} + end + + expect(group.examples[0].execution_result.status).to eq(:pending) + end end - expect(group.examples[0].execution_result.status).to eq(:pending) - expect(group.examples[1].execution_result.status).to eq(:passed) - expect(group.examples[2].execution_result.status).to eq(:pending) - expect(group.examples[3].execution_result.status).to eq(:passed) + it_behaves_like 'skipped in project', 'STAGING' + it_behaves_like 'skipped in project', 'CANARY' + it_behaves_like 'skipped in project', 'PRODUCTION' end end @@ -368,8 +391,8 @@ RSpec.describe QA::Specs::Helpers::Quarantine do it 'runs on a custom environment' do group = describe_successfully do - it('runs on release gitlab net', only: { tld: '.net', subdomain: :release, domain: 'gitlab' } ) {} - it('does not run on release', only: :production ) {} + it('runs on release gitlab net', only: { tld: '.net', subdomain: :release, domain: 'gitlab' }) {} + it('does not run on release', only: :production) {} end expect(group.examples.first.execution_result.status).to eq(:passed) @@ -384,7 +407,7 @@ RSpec.describe QA::Specs::Helpers::Quarantine do it 'runs on production' do group = describe_successfully do - it('runs on prod', only: :production ) {} + it('runs on prod', only: :production) {} it('does not run in prod', only: { subdomain: :staging }) {} it('runs in prod and staging', only: { subdomain: /(staging.)?/, domain: 'gitlab' }) {} end @@ -412,7 +435,7 @@ RSpec.describe QA::Specs::Helpers::Quarantine do it 'runs on any pipeline' do group = describe_successfully do - it('runs given a single named pipeline', only: { pipeline: :nightly } ) {} + it('runs given a single named pipeline', only: { pipeline: :nightly }) {} it('runs given an array of pipelines', only: { pipeline: [:canary, :not_nightly] }) {} end @@ -431,9 +454,9 @@ RSpec.describe QA::Specs::Helpers::Quarantine do it 'runs on default branch pipelines' do group = describe_successfully do - it('runs on master pipeline given a single pipeline', only: { pipeline: :master } ) {} + it('runs on master pipeline given a single pipeline', only: { pipeline: :master }) {} it('runs in master given an array of pipelines', only: { pipeline: [:canary, :master] }) {} - it('does not run in non-default pipelines', only: { pipeline: [:nightly, :not_nightly, :not_master] } ) {} + it('does not run in non-default pipelines', only: { pipeline: [:nightly, :not_nightly, :not_master] }) {} end aggregate_failures do @@ -452,8 +475,8 @@ RSpec.describe QA::Specs::Helpers::Quarantine do it 'runs on designated pipeline' do group = describe_successfully do - it('runs on nightly', only: { pipeline: :nightly } ) {} - it('does not run in not_nightly', only: { pipeline: :not_nightly } ) {} + it('runs on nightly', only: { pipeline: :nightly }) {} + it('does not run in not_nightly', only: { pipeline: :not_nightly }) {} it('runs on nightly given an array', only: { pipeline: [:canary, :nightly] }) {} it('does not run in not_nightly given an array', only: { pipeline: [:not_nightly, :canary] }) {} end diff --git a/qa/spec/support/matchers/have_matcher.rb b/qa/spec/support/matchers/have_matcher.rb index 43ae27f8796..81288b97e6f 100644 --- a/qa/spec/support/matchers/have_matcher.rb +++ b/qa/spec/support/matchers/have_matcher.rb @@ -15,6 +15,7 @@ module Matchers pipeline related_issue_item snippet_description + tag ].each do |predicate| RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs| match do |page_object| diff --git a/qa/spec/support/ssh_spec.rb b/qa/spec/support/ssh_spec.rb index f4d382f8adc..2edff824fd6 100644 --- a/qa/spec/support/ssh_spec.rb +++ b/qa/spec/support/ssh_spec.rb @@ -26,6 +26,16 @@ RSpec.describe QA::Support::SSH do end end + context 'when no port specified in https uri' do + let(:uri) { 'https://foo.com' } + + it 'does not provide port in ssh command' do + expect(ssh).to receive(:run).with(expected_ssh_command_no_port, any_args).and_return(result) + + call_method + end + end + context 'when port 80 specified in uri' do let(:uri) { 'http://foo.com:80' } @@ -86,6 +96,18 @@ RSpec.describe QA::Support::SSH do end end + context 'when running on a review app in CI' do + let(:uri) { 'https://gitlab-review.app' } + + before do + allow(QA::Runtime::Env).to receive(:running_in_ci?).and_return(true) + end + + it 'returns git user' do + expect(ssh.send(:git_user)).to eq('git') + end + end + context 'when running against environment on a port other than 80 or 443' do let(:uri) { 'http://localhost:3000' } diff --git a/qa/tls_certificates/authority/ca.crt b/qa/tls_certificates/authority/ca.crt new file mode 100644 index 00000000000..8812d718ede --- /dev/null +++ b/qa/tls_certificates/authority/ca.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFizCCA3OgAwIBAgIUJBX+SiNuqUYjf33EuMObynTcKx0wDQYJKoZIhvcNAQEL +BQAwVTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xGTAXBgNVBAoMEEdpdExhYiBBdXRob3JpdHkwHhcNMjEw +MjExMDYyMTUzWhcNMzEwMjA5MDYyMTUzWjBVMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEZMBcGA1UECgwQ +R2l0TGFiIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALxpL/dfJwhIWGYK93ovIVj+gqvJI58Sga7eWlp2LpuFQZETjJBA79R0gcwr42ph +ch1BuxoxkYizM8sqIyFa5aW8SCWmO+IQzedKWZv+UWZg6pG+Fzyp6GO1wmXZdeXw +xRiXp2qV+rqA8DaL2g1c2u6TB/mcfNTrfsSP3045yQW+4mdNx/g4wBf0dMpHe5DJ +5jIvowa2O0kesq1UQ1eM9IMNSwW8fMQenV+F41LWE1NO726ksglih9TQjAIrlX6I +XVmw0lgYegt2G+YQJ68nf2UmvK+C0KqzTZgqmzNMQnuSiJXMnlZvlgdLXTyTWCuq +tyxp3+Z2Wshk/Cghic2MCm2XwWBIqYGu+20qoPM61c3Rk5++2VOcajD1yCYxVtM2 +akVP/e9arlCdi/CtJh3qvOlj9Uhs6a1UR/ZngpPLOa3r4JazZc04pK7q3ZynmoUL +oDPF9AKOikvhYKxTYsgZ65VhI3myTBjQFk9qX++z2HF9UImGblqUtckn3eIoI8n2 +wd4pO/OFeFSXpR7jMywCwXTpC8yTTbuVzHEz8H83/qtsbjtw961g4kb8Vzh1zy73 +PFiJNI7hZlP1I5dqNTOAv+GnwiSnF28i7xbdkIfmjYqwcwGhAO7rjifq8ygBH3sF +0I/6dnw4BvNlSgSJlJ1+2eH1uiaikpuOGtQAttz536dNAgMBAAGjUzBRMB0GA1Ud +DgQWBBQSNZ46Xu4rfGVIPIQJpduWHJ+AQjAfBgNVHSMEGDAWgBQSNZ46Xu4rfGVI +PIQJpduWHJ+AQjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQC1 +4REbDKF1tdYeCxpmQHXLHu7OkrgKIea6eKxf8mI+ZsJA4iNr3aiZYU5Wm+uAq3mK +x6mrQRaqoo6JNbie5Tw8fU2c71EO4t3vwB7YOsBtcV0YrMtg316UE0lk+6kL0TRD +tV7dAGCB3wXZVLPolC26/kecc258p6h4rCJ8Gi6FsIKGBisTFk3QOdLG3DQNulPF +2bZaJjs/6HqLL+QMdChUfJP3i71LdNxzwXNPbjJ3fm5f9RNeOcWo6oI2fkQArl2i +vcP/QtuqeOoJusbeL9zaF/EFlOggjkEtzFWCB05d6J7dMKYp5Lg4eMZQHPw6ycdk +Ek/XTupvBk+8UiXeHLvX6II9zww9+gm3uycPiO/CSPNw4yQBPPGW+30WznbedsuY +m79rxuq50KYvseJSPv7lw8v77ajQbRQzFi4frAFR+A1g8FArLxdpIptI9E+ekmaB +UMhCmi17mEuBtj8Hc3VrokTFtAJV6YNIk1X+Q/zLh4TaR5mm6C/82j/ZvkSm5OX7 +TfaayRWDr7AfDKeG5Amt52BmYncsPUXnOofSz5UkqpeOWr0rKn+A7f2BeUP9R2nY +6DncxYA+yOgU7RpnMBscsW6SkVIMJKFrfeZ1SbvZGRJd4L1PwfeaU+3yEG6oAfMa +CFquw23nWhjvUlnCe9BaUiEtOJ2ruGjdu2sdvXPcnw== +-----END CERTIFICATE----- diff --git a/qa/tls_certificates/authority/ca.key b/qa/tls_certificates/authority/ca.key new file mode 100644 index 00000000000..b465551e841 --- /dev/null +++ b/qa/tls_certificates/authority/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/qa/tls_certificates/authority/ca.pem b/qa/tls_certificates/authority/ca.pem new file mode 100644 index 00000000000..51c2c7a20e7 --- /dev/null +++ b/qa/tls_certificates/authority/ca.pem @@ -0,0 +1,83 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFizCCA3OgAwIBAgIUJBX+SiNuqUYjf33EuMObynTcKx0wDQYJKoZIhvcNAQEL +BQAwVTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xGTAXBgNVBAoMEEdpdExhYiBBdXRob3JpdHkwHhcNMjEw +MjExMDYyMTUzWhcNMzEwMjA5MDYyMTUzWjBVMQswCQYDVQQGEwJVUzETMBEGA1UE +CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEZMBcGA1UECgwQ +R2l0TGFiIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALxpL/dfJwhIWGYK93ovIVj+gqvJI58Sga7eWlp2LpuFQZETjJBA79R0gcwr42ph +ch1BuxoxkYizM8sqIyFa5aW8SCWmO+IQzedKWZv+UWZg6pG+Fzyp6GO1wmXZdeXw +xRiXp2qV+rqA8DaL2g1c2u6TB/mcfNTrfsSP3045yQW+4mdNx/g4wBf0dMpHe5DJ +5jIvowa2O0kesq1UQ1eM9IMNSwW8fMQenV+F41LWE1NO726ksglih9TQjAIrlX6I +XVmw0lgYegt2G+YQJ68nf2UmvK+C0KqzTZgqmzNMQnuSiJXMnlZvlgdLXTyTWCuq +tyxp3+Z2Wshk/Cghic2MCm2XwWBIqYGu+20qoPM61c3Rk5++2VOcajD1yCYxVtM2 +akVP/e9arlCdi/CtJh3qvOlj9Uhs6a1UR/ZngpPLOa3r4JazZc04pK7q3ZynmoUL +oDPF9AKOikvhYKxTYsgZ65VhI3myTBjQFk9qX++z2HF9UImGblqUtckn3eIoI8n2 +wd4pO/OFeFSXpR7jMywCwXTpC8yTTbuVzHEz8H83/qtsbjtw961g4kb8Vzh1zy73 +PFiJNI7hZlP1I5dqNTOAv+GnwiSnF28i7xbdkIfmjYqwcwGhAO7rjifq8ygBH3sF +0I/6dnw4BvNlSgSJlJ1+2eH1uiaikpuOGtQAttz536dNAgMBAAGjUzBRMB0GA1Ud +DgQWBBQSNZ46Xu4rfGVIPIQJpduWHJ+AQjAfBgNVHSMEGDAWgBQSNZ46Xu4rfGVI +PIQJpduWHJ+AQjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQC1 +4REbDKF1tdYeCxpmQHXLHu7OkrgKIea6eKxf8mI+ZsJA4iNr3aiZYU5Wm+uAq3mK +x6mrQRaqoo6JNbie5Tw8fU2c71EO4t3vwB7YOsBtcV0YrMtg316UE0lk+6kL0TRD +tV7dAGCB3wXZVLPolC26/kecc258p6h4rCJ8Gi6FsIKGBisTFk3QOdLG3DQNulPF +2bZaJjs/6HqLL+QMdChUfJP3i71LdNxzwXNPbjJ3fm5f9RNeOcWo6oI2fkQArl2i +vcP/QtuqeOoJusbeL9zaF/EFlOggjkEtzFWCB05d6J7dMKYp5Lg4eMZQHPw6ycdk +Ek/XTupvBk+8UiXeHLvX6II9zww9+gm3uycPiO/CSPNw4yQBPPGW+30WznbedsuY +m79rxuq50KYvseJSPv7lw8v77ajQbRQzFi4frAFR+A1g8FArLxdpIptI9E+ekmaB +UMhCmi17mEuBtj8Hc3VrokTFtAJV6YNIk1X+Q/zLh4TaR5mm6C/82j/ZvkSm5OX7 +TfaayRWDr7AfDKeG5Amt52BmYncsPUXnOofSz5UkqpeOWr0rKn+A7f2BeUP9R2nY +6DncxYA+yOgU7RpnMBscsW6SkVIMJKFrfeZ1SbvZGRJd4L1PwfeaU+3yEG6oAfMa +CFquw23nWhjvUlnCe9BaUiEtOJ2ruGjdu2sdvXPcnw== +-----END CERTIFICATE----- |