diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-20 18:19:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-20 18:19:03 +0300 |
commit | 14bd84b61276ef29b97d23642d698de769bacfd2 (patch) | |
tree | f9eba90140c1bd874211dea17750a0d422c04080 /qa | |
parent | 891c388697b2db0d8ee0c8358a9bdbf6dc56d581 (diff) |
Add latest changes from gitlab-org/gitlab@15-10-stable-eev15.10.0-rc42
Diffstat (limited to 'qa')
110 files changed, 2179 insertions, 731 deletions
diff --git a/qa/Gemfile b/qa/Gemfile index 9e41b5ddeed..9e35c619c5b 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -2,14 +2,14 @@ source 'https://rubygems.org' -gem 'gitlab-qa', '~> 9', require: 'gitlab/qa' +gem 'gitlab-qa', '~> 9', '>= 9.1.2', require: 'gitlab/qa' gem 'activesupport', '~> 6.1.7.2' # This should stay in sync with the root's Gemfile gem 'allure-rspec', '~> 2.20.0' gem 'capybara', '~> 3.38.0' gem 'capybara-screenshot', '~> 1.0.26' gem 'rake', '~> 13', '>= 13.0.6' gem 'rspec', '~> 3.12' -gem 'selenium-webdriver', '~> 4.8' +gem 'selenium-webdriver', '~> 4.8', '>= 4.8.1' gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default gem 'rest-client', '~> 2.1.0' gem 'rspec-retry', '~> 0.6.2', require: 'rspec/retry' @@ -21,8 +21,8 @@ gem 'rotp', '~> 6.2.2' gem 'parallel', '~> 1.22', '>= 1.22.1' gem 'rainbow', '~> 3.1.1' gem 'rspec-parameterized', '~> 1.0.0' -gem 'octokit', '~> 6.0.1' -gem "faraday-retry", "~> 2.0" +gem 'octokit', '~> 6.1.0' +gem "faraday-retry", "~> 2.1" gem 'webdrivers', '~> 5.2' gem 'zeitwerk', '~> 2.6', '>= 2.6.7' gem 'influxdb-client', '~> 2.9' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index d544aa685a5..c3faf5841fc 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -51,7 +51,7 @@ GEM chemlab (~> 0.4) coderay (1.1.2) colorize (0.8.1) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.2) confiner (0.4.0) gitlab (>= 4.17) zeitwerk (>= 2.5, < 3) @@ -69,7 +69,7 @@ GEM faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-net_http (3.0.0) - faraday-retry (2.0.0) + faraday-retry (2.1.0) faraday (~> 2.0) ffi (1.15.5) ffi-compiler (1.0.1) @@ -102,11 +102,12 @@ GEM gitlab (4.18.0) httparty (~> 0.18) terminal-table (>= 1.5.1) - gitlab-qa (9.0.0) + gitlab-qa (9.1.2) activesupport (~> 6.1) gitlab (~> 4.18.0) http (~> 5.0) nokogiri (~> 1.10) + parallel (>= 1, < 2) rainbow (>= 3, < 4) table_print (= 1.5.7) zeitwerk (>= 2, < 3) @@ -151,8 +152,8 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) http-form_data (2.3.0) - httparty (0.20.0) - mime-types (~> 3.0) + httparty (0.21.0) + mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) httpclient (2.8.3) i18n (1.12.0) @@ -173,17 +174,17 @@ GEM method_source (1.0.0) mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) + mime-types-data (3.2023.0218.1) mini_mime (1.1.0) mini_portile2 (2.8.1) - minitest (5.17.0) + minitest (5.18.0) multi_json (1.15.0) multi_xml (0.6.0) netrc (0.11.0) nokogiri (1.14.2) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (6.0.1) + octokit (6.1.0) faraday (>= 1, < 3) sawyer (~> 0.9) oj (3.13.23) @@ -203,7 +204,7 @@ GEM pry-byebug (3.10.1) byebug (~> 11.0) pry (>= 0.13, < 0.15) - public_suffix (5.0.0) + public_suffix (5.0.1) racc (1.6.2) rack (2.2.3.1) rack-test (1.1.0) @@ -259,7 +260,7 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - selenium-webdriver (4.8.0) + selenium-webdriver (4.8.1) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -274,13 +275,13 @@ GEM terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) - tzinfo (2.0.5) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (2.3.0) + unicode-display_width (2.4.2) unparser (0.6.5) diff-lcs (~> 1.3) parser (>= 3.1.0) @@ -314,14 +315,14 @@ DEPENDENCIES confiner (~> 0.4) deprecation_toolkit (~> 2.0.3) faker (~> 3.1, >= 3.1.1) - faraday-retry (~> 2.0) + faraday-retry (~> 2.1) fog-core (= 2.1.0) fog-google (~> 1.19) - gitlab-qa (~> 9) + gitlab-qa (~> 9, >= 9.1.2) influxdb-client (~> 2.9) knapsack (~> 4.0) nokogiri (~> 1.14, >= 1.14.2) - octokit (~> 6.0.1) + octokit (~> 6.1.0) parallel (~> 1.22, >= 1.22.1) parallel_tests (~> 4.2) pry-byebug (~> 3.10.1) @@ -334,7 +335,7 @@ DEPENDENCIES rspec-retry (~> 0.6.2) rspec_junit_formatter (~> 0.6.0) ruby-debug-ide (~> 0.7.3) - selenium-webdriver (~> 4.8) + selenium-webdriver (~> 4.8, >= 4.8.1) slack-notifier (~> 2.4) terminal-table (~> 3.0.2) warning (~> 1.3) @@ -342,4 +343,4 @@ DEPENDENCIES zeitwerk (~> 2.6, >= 2.6.7) BUNDLED WITH - 2.4.6 + 2.4.8 diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb index 3cb501efe13..22e61c97bdb 100644 --- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb +++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb @@ -23,12 +23,11 @@ module Gitlab # Storage section link :storage_tab link :purchase_more_storage - div :used_storage_message + div :namespace_usage_total div :group_usage_message div :dependency_proxy_usage span :dependency_proxy_size div :container_registry_usage - div :project_storage_used div :project div :storage_type_legend span :container_registry_size @@ -82,7 +82,8 @@ module QA "jetbrains" => "JetBrains", "vscode" => "VSCode", "registry_with_cdn" => "RegistryWithCDN", - "fips" => "FIPS" + "fips" => "FIPS", + "ci_cd_settings" => "CICDSettings" ) loader.setup diff --git a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb deleted file mode 100644 index e6ec4528d0d..00000000000 --- a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb +++ /dev/null @@ -1,111 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: gitlab-agent ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gitlab-agent -spec: - replicas: 1 - selector: - matchLabels: - app: gitlab-agent - template: - metadata: - labels: - app: gitlab-agent - spec: - serviceAccountName: gitlab-agent - containers: - - name: agent - image: "registry.gitlab.com/gitlab-org/cluster-integration/gitlab-agent/agentk:<%= Runtime::Env.gitlab_agentk_version %>" - args: - - --token-file=/config/token - - --kas-address - - "<%= kas_wss_address %>" - <% if QA::Runtime::Env.qa_cookies.to_s.include?("gitlab_canary=true") %> - - --kas-header - - "Cookie: gitlab_canary=true" - <% end %> - volumeMounts: - - name: token-volume - mountPath: /config - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: SERVICE_ACCOUNT_NAME - valueFrom: - fieldRef: - fieldPath: spec.serviceAccountName - volumes: - - name: token-volume - secret: - secretName: gitlab-agent-token - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: gitlab-agent-write -rules: - - resources: - - "*" - apiGroups: - - "*" - verbs: - - create - - update - - delete - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: gitlab-agent-write-binding -roleRef: - name: gitlab-agent-write - kind: ClusterRole - apiGroup: rbac.authorization.k8s.io -subjects: - - name: gitlab-agent - kind: ServiceAccount - namespace: default ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: gitlab-agent-read -rules: - - resources: - - "*" - apiGroups: - - "*" - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: gitlab-agent-read-binding -roleRef: - name: gitlab-agent-read - kind: ClusterRole - apiGroup: rbac.authorization.k8s.io -subjects: - - name: gitlab-agent - kind: ServiceAccount - namespace: default diff --git a/qa/qa/fixtures/mocks/import/github.yml b/qa/qa/fixtures/mocks/import/github.yml index 9cabdee1025..b8e137abfbb 100644 --- a/qa/qa/fixtures/mocks/import/github.yml +++ b/qa/qa/fixtures/mocks/import/github.yml @@ -2741,3 +2741,77 @@ }, "url": "https://api.github.com/repos/gitlab-qa-github/import-test/branches/release/protection" } + +- request: + path: /repos/gitlab-qa-github/import-test/collaborators + method: GET + query_params: + page: '1' + per_page: '100' + headers: + Host: api.github.com + response: + status: 200 + headers: + Content-Type: application/json; charset=utf-8 + X-Ratelimit-Limit: '5000' + X-Ratelimit-Remaining: '5000' + body: | + [ + { + "avatar_url": "https://avatars.githubusercontent.com/u/40021320?v=4", + "events_url": "https://api.github.com/users/gitlab-qa/events{/privacy}", + "followers_url": "https://api.github.com/users/gitlab-qa/followers", + "following_url": "https://api.github.com/users/gitlab-qa/following{/other_user}", + "gists_url": "https://api.github.com/users/gitlab-qa/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/gitlab-qa", + "id": 40021320, + "login": "gitlab-qa", + "node_id": "MDQ6VXNlcjQwMDIxMzIw", + "organizations_url": "https://api.github.com/users/gitlab-qa/orgs", + "permissions": { + "admin": false, + "maintain": false, + "pull": true, + "push": true, + "triage": true + }, + "received_events_url": "https://api.github.com/users/gitlab-qa/received_events", + "repos_url": "https://api.github.com/users/gitlab-qa/repos", + "role_name": "write", + "site_admin": false, + "starred_url": "https://api.github.com/users/gitlab-qa/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gitlab-qa/subscriptions", + "type": "User", + "url": "https://api.github.com/users/gitlab-qa" + }, + { + "avatar_url": "https://avatars.githubusercontent.com/u/59606922?v=4", + "events_url": "https://api.github.com/users/gitlab-qa-github/events{/privacy}", + "followers_url": "https://api.github.com/users/gitlab-qa-github/followers", + "following_url": "https://api.github.com/users/gitlab-qa-github/following{/other_user}", + "gists_url": "https://api.github.com/users/gitlab-qa-github/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/gitlab-qa-github", + "id": 59606922, + "login": "gitlab-qa-github", + "node_id": "MDQ6VXNlcjU5NjA2OTIy", + "organizations_url": "https://api.github.com/users/gitlab-qa-github/orgs", + "permissions": { + "admin": true, + "maintain": true, + "pull": true, + "push": true, + "triage": true + }, + "received_events_url": "https://api.github.com/users/gitlab-qa-github/received_events", + "repos_url": "https://api.github.com/users/gitlab-qa-github/repos", + "role_name": "admin", + "site_admin": false, + "starred_url": "https://api.github.com/users/gitlab-qa-github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gitlab-qa-github/subscriptions", + "type": "User", + "url": "https://api.github.com/users/gitlab-qa-github" + } + ] diff --git a/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/gitlab_ci.yaml.erb b/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/gitlab_ci.yaml.erb new file mode 100644 index 00000000000..394c6689eef --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/gitlab_ci.yaml.erb @@ -0,0 +1,8 @@ +install: + image: maven:3.6-jdk-11 + script: + - "mvn install -U -s settings.xml" + only: + - "<%= imported_project.default_branch %>" + tags: + - "runner-for-<%= imported_project.group.name %>"
\ No newline at end of file diff --git a/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/settings.xml.erb b/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/settings.xml.erb new file mode 100644 index 00000000000..4bcd63b3c6d --- /dev/null +++ b/qa/qa/fixtures/package_managers/maven/group/consumer/request_forwarding/settings.xml.erb @@ -0,0 +1,23 @@ +<settings> + <servers> + <server> + <id>central-proxy</id> + <configuration> + <httpHeaders> + <property> + <name>Private-Token</name> + <value><%= personal_access_token %></value> + </property> + </httpHeaders> + </configuration> + </server> + </servers> + <mirrors> + <mirror> + <id>central-proxy</id> + <name>GitLab proxy of central repo</name> + <url><%= gitlab_address_with_port %>/api/v4/groups/<%= imported_project.group.id %>/-/packages/maven</url> + <mirrorOf>central</mirrorOf> + </mirror> + </mirrors> +</settings>
\ No newline at end of file diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb index 564a51ee483..2702b52f2ef 100644 --- a/qa/qa/flow/login.rb +++ b/qa/qa/flow/login.rb @@ -21,8 +21,8 @@ module QA def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false) Page::Main::Login.perform { |p| p.redirect_to_login_page(address) } - unless Page::Main::Login.perform(&:on_login_page?) - Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?) + if !Page::Main::Login.perform(&:on_login_page?) && Page::Main::Menu.perform(&:signed_in?) + Page::Main::Menu.perform(&:sign_out) end Page::Main::Login.perform do |login| diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb index fb6a5425a6e..0765a8758ec 100644 --- a/qa/qa/flow/pipeline.rb +++ b/qa/qa/flow/pipeline.rb @@ -24,6 +24,14 @@ module QA index.wait_for_latest_pipeline(status: status, wait: wait) end end + + def visit_pipeline_job_page(job_name:, pipeline: nil) + pipeline.visit! unless pipeline.nil? + + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job(job_name) + end + end end end end diff --git a/qa/qa/flow/saml.rb b/qa/qa/flow/saml.rb index 8a0ebb9f551..1cf29e75c67 100644 --- a/qa/qa/flow/saml.rb +++ b/qa/qa/flow/saml.rb @@ -19,8 +19,6 @@ module QA end def enable_saml_sso(group, saml_idp_service, enforce_sso: false, default_membership_role: 'Guest') - Runtime::Feature.enable(:group_administration_nav_item) - page.visit Runtime::Scenario.gitlab_address Page::Main::Login.perform(&:sign_in_using_credentials) unless Page::Main::Menu.perform(&:signed_in?) diff --git a/qa/qa/page/component/dropdown.rb b/qa/qa/page/component/dropdown.rb index 01ef3533ff8..767cd40daa2 100644 --- a/qa/qa/page/component/dropdown.rb +++ b/qa/qa/page/component/dropdown.rb @@ -4,8 +4,18 @@ module QA module Page module Component module Dropdown - def select_item(item_text) - find('li.gl-new-dropdown-item', text: item_text, match: :prefer_exact).click + # Find and click item using css selector and matching text + # If item_text is not provided, select the first item that matches the given css selector + # + # @param [String] item_text + # @param [String] css - css selector of the item + # @return [void] + def select_item(item_text, css: 'li.gl-new-dropdown-item') + if item_text + find(css, text: item_text, match: :prefer_exact).click + else + find(css, match: :first).click + end end def has_item?(item_text) @@ -65,8 +75,8 @@ module QA find('li.gl-new-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click end - def expand_select_list - find('.gl-new-dropdown-toggle').click + def expand_select_list(css: '.gl-new-dropdown-toggle') + find(css).click end def wait_for_search_to_complete diff --git a/qa/qa/page/component/groups_filter.rb b/qa/qa/page/component/groups_filter.rb index ea91ced8679..14e49e53b75 100644 --- a/qa/qa/page/component/groups_filter.rb +++ b/qa/qa/page/component/groups_filter.rb @@ -16,10 +16,6 @@ module QA base.view 'app/assets/javascripts/groups/components/groups.vue' do element :groups_list_tree_container end - - base.view 'app/views/dashboard/_groups_head.html.haml' do - element :public_groups_tab - end end private @@ -29,14 +25,8 @@ module QA # @return [Boolean] whether a group with given name exists def has_filtered_group?(name) filter_group(name) - return true if page.has_link?(name, wait: 0) # element containing link to group - return false unless has_element?(:public_groups_tab, wait: 0) - - # Check public groups - click_element(:public_groups_tab) - filter_group(name) - page.has_link?(name, wait: 0) + page.has_link?(name, wait: 0) # element containing link to group end # Filter by group name diff --git a/qa/qa/page/group/sub_menus/common.rb b/qa/qa/page/group/sub_menus/common.rb index 2f8a3fdeb4e..3cbca3db359 100644 --- a/qa/qa/page/group/sub_menus/common.rb +++ b/qa/qa/page/group/sub_menus/common.rb @@ -21,7 +21,7 @@ module QA private def sidebar_element - :group_sidebar + QA::Runtime::Env.super_sidebar_enabled? ? :navbar : :group_sidebar end end end diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index f4f8820bc04..7532154f0cc 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -233,6 +233,7 @@ module QA terms.accept_terms if terms.visible? end + Page::Main::Menu.perform(&:enable_new_navigation) if Runtime::Env.super_sidebar_enabled? Page::Main::Menu.validate_elements_present! unless skip_page_validation end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 1e050d79e23..f86849061e8 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -6,21 +6,35 @@ module QA class Menu < Page::Base prepend Mobile::Page::Main::Menu if Runtime::Env.mobile_layout? - view 'app/views/layouts/header/_current_user_dropdown.html.haml' do - element :sign_out_link - element :edit_profile_link - element :user_profile_link - end + if QA::Runtime::Env.super_sidebar_enabled? + # Define alternative navbar (super sidebar) which does not yet implement all the same elements + view 'app/assets/javascripts/super_sidebar/components/super_sidebar.vue' do + element :navbar, required: true # TODO: rename to sidebar once it's default implementation + element :user_menu, required: !QA::Runtime::Env.mobile_layout? + element :user_avatar_content, required: !QA::Runtime::Env.mobile_layout? + end - view 'app/views/layouts/header/_default.html.haml' do - element :navbar, required: true - element :canary_badge_link - element :user_avatar_content, required: !QA::Runtime::Env.mobile_layout? - element :user_menu, required: !QA::Runtime::Env.mobile_layout? - element :stop_impersonation_link - element :issues_shortcut_button, required: !QA::Runtime::Env.mobile_layout? - element :merge_requests_shortcut_button, required: !QA::Runtime::Env.mobile_layout? - element :todos_shortcut_button, required: !QA::Runtime::Env.mobile_layout? + view 'app/assets/javascripts/super_sidebar/components/user_menu.vue' do + element :sign_out_link + element :edit_profile_link + end + else + view 'app/views/layouts/header/_default.html.haml' do + element :navbar, required: true + element :canary_badge_link + element :user_avatar_content, required: !QA::Runtime::Env.mobile_layout? + element :user_menu, required: !QA::Runtime::Env.mobile_layout? + element :stop_impersonation_link + element :issues_shortcut_button, required: !QA::Runtime::Env.mobile_layout? + element :merge_requests_shortcut_button, required: !QA::Runtime::Env.mobile_layout? + element :todos_shortcut_button, required: !QA::Runtime::Env.mobile_layout? + end + + view 'app/views/layouts/header/_current_user_dropdown.html.haml' do + element :sign_out_link + element :edit_profile_link + element :user_profile_link + end end view 'app/assets/javascripts/nav/components/top_nav_app.vue' do @@ -39,7 +53,6 @@ module QA element :admin_area_link element :projects_dropdown element :groups_dropdown - element :snippets_link element :menu_item_link end @@ -64,6 +77,10 @@ module QA element :global_new_project_link end + view 'app/assets/javascripts/nav/components/new_nav_toggle.vue' do + element :new_navigation_toggle + end + def go_to_groups within_groups_menu do click_element(:menu_item_link, title: 'View all groups') @@ -81,12 +98,18 @@ module QA end end + def go_to_snippets + click_element(:sidebar_menu_link, menu_item: 'Snippets') + end + def go_to_create_project click_element(:new_menu_toggle) click_element(:global_new_project_link) end def go_to_menu_dropdown_option(option_name) + return click_element(option_name) if QA::Runtime::Env.super_sidebar_enabled? + within_top_menu do click_element(:navbar_dropdown, title: 'Menu') click_element(option_name) @@ -211,6 +234,13 @@ module QA has_element?(:canary_badge_link) end + def enable_new_navigation + Runtime::Logger.info("Enabling super sidebar!") + return Runtime::Logger.info("Super sidebar is already enabled") if has_css?('[data-testid="super-sidebar"]') + + within_user_menu { click_element(:new_navigation_toggle) } + end + private def within_top_menu(&block) diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb index a1d91621090..90022616674 100644 --- a/qa/qa/page/merge_request/new.rb +++ b/qa/qa/page/merge_request/new.rb @@ -4,6 +4,8 @@ module QA module Page module MergeRequest class New < Page::Issuable::New + include QA::Page::Component::Dropdown + view 'app/views/shared/issuable/_form.html.haml' do element :issuable_create_button, required: true end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index df0c0ec4202..451a9c3ee6e 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -31,6 +31,7 @@ module QA view 'app/assets/javascripts/diffs/components/tree_list.vue' do element :file_tree_container + element :diff_tree_search end view 'app/assets/javascripts/diffs/components/diff_file_header.vue' do @@ -215,14 +216,25 @@ module QA def has_file?(file_name) open_file_tree + + return true if has_element?(:file_name_content, file_name: file_name) + + # Since the file tree uses virtual scrolling, search for file in case it is outside of viewport + search_file_tree(file_name) has_element?(:file_name_content, file_name: file_name) end def has_no_file?(file_name) - open_file_tree + # Since the file tree uses virtual scrolling, search for file to ensure non-existence + search_file_tree(file_name) has_no_element?(:file_name_content, file_name: file_name) end + def search_file_tree(file_name) + open_file_tree + fill_element(:diff_tree_search, file_name) + end + def open_file_tree click_element(:file_tree_button) unless has_element?(:file_tree_container) end @@ -233,6 +245,17 @@ module QA has_element?(:merge_button) end + def has_no_merge_button? + refresh + + has_no_element?(:merge_button) + end + + RSpec::Matchers.define :have_merge_button do + match(&:has_merge_button?) + match_when_negated(&:has_no_merge_button?) + end + def has_pipeline_status?(text) # Pipelines can be slow, so we wait a bit longer than the usual 10 seconds wait_until(max_duration: 120, sleep_interval: 5, reload: true) do @@ -386,6 +409,7 @@ module QA click_element(:dropdown_button) click_element(:edit_in_ide_button) end + page.driver.browser.switch_to.window(page.driver.browser.window_handles.last) end def add_suggestion_to_diff(suggestion, line) diff --git a/qa/qa/page/profile/menu.rb b/qa/qa/page/profile/menu.rb index 947fa2fec0f..651603a77db 100644 --- a/qa/qa/page/profile/menu.rb +++ b/qa/qa/page/profile/menu.rb @@ -8,25 +8,35 @@ module QA # since tablets have the regular top navigation bar but still close the left nav prepend QA::Mobile::Page::SubMenus::Common if QA::Runtime::Env.remote_mobile_device_name - view 'app/views/layouts/nav/sidebar/_profile.html.haml' do - element :access_token_link, 'link_to profile_personal_access_tokens_path' # rubocop:disable QA/ElementWithPattern - element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern - element :top_level_items, '.sidebar-top-level-items' # rubocop:disable QA/ElementWithPattern - element :ssh_keys, 'SSH Keys' # rubocop:disable QA/ElementWithPattern + view 'lib/sidebars/user_settings/menus/access_tokens_menu.rb' do + element :access_token_link + end + + view 'lib/sidebars/user_settings/menus/ssh_keys_menu.rb' do + element :ssh_keys_link + end + + view 'lib/sidebars/user_settings/menus/emails_menu.rb' do element :profile_emails_link + end + + view 'lib/sidebars/user_settings/menus/password_menu.rb' do element :profile_password_link + end + + view 'lib/sidebars/user_settings/menus/account_menu.rb' do element :profile_account_link end def click_access_tokens within_sidebar do - click_link('Access Tokens') + click_element(:access_token_link) end end def click_ssh_keys within_sidebar do - click_link('SSH Keys') + click_element(:ssh_keys_link) end end diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb index 24fd34b4d22..444c67cfe4f 100644 --- a/qa/qa/page/project/job/show.rb +++ b/qa/qa/page/project/job/show.rb @@ -68,6 +68,14 @@ module QA end end + def has_locked_artifact? + has_text?('will not be deleted') + end + + def has_unlocked_artifact? + has_text?('will be removed') + end + private def loaded?(wait: 60) diff --git a/qa/qa/page/project/monitor/alerts/index.rb b/qa/qa/page/project/monitor/alerts/index.rb index 1363fb32498..1738ce170f2 100644 --- a/qa/qa/page/project/monitor/alerts/index.rb +++ b/qa/qa/page/project/monitor/alerts/index.rb @@ -13,6 +13,18 @@ module QA def has_alert_with_title?(title) has_link?(title, wait: 5) end + + def go_to_alert(title) + click_link_with_text(title) + end + + def has_no_alert_with_title?(title) + has_no_link?(title, wait: 5) + end + + def go_to_tab(name) + click_link_with_text(name) + end end end end diff --git a/qa/qa/page/project/monitor/alerts/show.rb b/qa/qa/page/project/monitor/alerts/show.rb new file mode 100644 index 00000000000..1f3c52d8988 --- /dev/null +++ b/qa/qa/page/project/monitor/alerts/show.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module QA + module Page + module Project + module Monitor + module Alerts + class Show < Page::Base + view 'app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue' do + element :alert_system_note_container + end + + def go_to_activity_feed_tab + click_link_with_text('Activity feed') + end + + def has_system_note?(text) + has_element?(:alert_system_note_container, text: text) + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/monitor/incidents/index.rb b/qa/qa/page/project/monitor/incidents/index.rb index 1b30e484723..04cb23da389 100644 --- a/qa/qa/page/project/monitor/incidents/index.rb +++ b/qa/qa/page/project/monitor/incidents/index.rb @@ -15,8 +15,16 @@ module QA click_element :create_incident_button end - def has_incident?(wait: Support::Repeater::DEFAULT_MAX_WAIT_TIME) - wait_until(max_duration: wait) { has_element?(:incident_link) } + def has_incident?(wait: Support::Repeater::DEFAULT_MAX_WAIT_TIME, title: nil) + wait_until(max_duration: wait) { has_element?(:incident_link, text: title) } + end + + def has_no_incident?(title: nil) + has_no_element?(:incident_link, text: title) + end + + def go_to_tab(tab) + click_link_with_text(tab) end end end diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index e4511ababfd..25d62ac59af 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -44,6 +44,17 @@ module QA element :jobs_dropdown_menu end + view 'app/views/layouts/nav/_breadcrumbs.html.haml' do + element :breadcrumb_links_content + element :breadcrumb_current_link + end + + def pipeline_id + within_element(:breadcrumb_links_content) do + find_element(:breadcrumb_current_link).text.delete_prefix('#') + end + end + def running?(wait: 0) within_element(:pipeline_header) do page.has_content?('running', wait: wait) diff --git a/qa/qa/page/project/settings/alerts.rb b/qa/qa/page/project/settings/alerts.rb index 901a668f082..3ff4ef20bde 100644 --- a/qa/qa/page/project/settings/alerts.rb +++ b/qa/qa/page/project/settings/alerts.rb @@ -5,11 +5,12 @@ module QA module Project module Settings class Alerts < Page::Base + include ::QA::Page::Component::Dropdown + view 'app/assets/javascripts/alerts_settings/components/alerts_form.vue' do element :create_incident_checkbox element :incident_templates_dropdown element :save_changes_button - element :incident_templates_item element :enable_email_notification_checkbox end @@ -42,7 +43,7 @@ module QA def select_issue_template(template) click_element(:incident_templates_dropdown) within_element :incident_templates_dropdown do - find_element(:incident_templates_item, text: template).click + select_item(template) end end diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 3c2b8d56f1d..b89e17910c9 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -163,10 +163,12 @@ module QA def open_web_ide! click_element(:web_ide_button) + page.driver.browser.switch_to.window(page.driver.browser.window_handles.last) end def open_web_ide_via_shortcut page.driver.send_keys('.') + page.driver.browser.switch_to.window(page.driver.browser.window_handles.last) end def has_edit_fork_button? diff --git a/qa/qa/page/project/sub_menus/common.rb b/qa/qa/page/project/sub_menus/common.rb index 112f49a90ee..79054ec9802 100644 --- a/qa/qa/page/project/sub_menus/common.rb +++ b/qa/qa/page/project/sub_menus/common.rb @@ -29,7 +29,7 @@ module QA private def sidebar_element - :project_sidebar + QA::Runtime::Env.super_sidebar_enabled? ? :navbar : :project_sidebar end end end diff --git a/qa/qa/page/project/sub_menus/monitor.rb b/qa/qa/page/project/sub_menus/monitor.rb index 27fb58fb146..00774261467 100644 --- a/qa/qa/page/project/sub_menus/monitor.rb +++ b/qa/qa/page/project/sub_menus/monitor.rb @@ -31,6 +31,22 @@ module QA end end + def go_to_monitor_on_call_schedules + hover_monitor do + within_submenu do + click_element(:sidebar_menu_item_link, menu_item: 'On-call Schedules') + end + end + end + + def go_to_monitor_escalation_policies + hover_monitor do + within_submenu do + click_element(:sidebar_menu_item_link, menu_item: 'Escalation Policies') + end + end + end + private def hover_monitor diff --git a/qa/qa/page/project/sub_menus/repository.rb b/qa/qa/page/project/sub_menus/repository.rb index f9d55c0009c..b8ebaa10a49 100644 --- a/qa/qa/page/project/sub_menus/repository.rb +++ b/qa/qa/page/project/sub_menus/repository.rb @@ -40,7 +40,7 @@ module QA def go_to_repository_contributors hover_repository do within_submenu do - click_element(:sidebar_menu_item_link, menu_item: 'Contributors') + click_element(:sidebar_menu_item_link, menu_item: 'Contributor statistics') end end end diff --git a/qa/qa/page/user/show.rb b/qa/qa/page/user/show.rb index ad2de331ad9..9f5f0fae9bc 100644 --- a/qa/qa/page/user/show.rb +++ b/qa/qa/page/user/show.rb @@ -6,7 +6,7 @@ module QA class Show < Page::Base view 'app/views/users/show.html.haml' do element :follow_user_link - element :following_link + element :following_tab end view 'app/views/shared/users/_user.html.haml' do @@ -21,8 +21,8 @@ module QA click_element(:follow_user_link) end - def click_following_link - click_element(:following_link) + def click_following_tab + click_element(:following_tab) end def click_user_link(username) diff --git a/qa/qa/resource/api_fabricator.rb b/qa/qa/resource/api_fabricator.rb index d7a220bc83f..cbb68cccdf1 100644 --- a/qa/qa/resource/api_fabricator.rb +++ b/qa/qa/resource/api_fabricator.rb @@ -10,6 +10,7 @@ module QA include Support::API include Errors + attr_reader :api_fabrication_http_method attr_writer :api_client attr_accessor :api_user, :api_resource, :api_response @@ -49,22 +50,6 @@ module QA end end - def api_put(body = api_put_body) - response = put( - Runtime::API::Request.new(api_client, api_put_path).url, - body) - - unless response.code == HTTP_STATUS_OK - raise ResourceFabricationFailedError, "Updating #{self.class.name} using the API failed (#{response.code}) with `#{response}`.\n#{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])}" - end - - process_api_response(parse_body(response)) - end - - def api_fabrication_http_method - @api_fabrication_http_method ||= :post - end - # Checks if a resource already exists # # @return [Boolean] true if the resource returns HTTP status code 200 @@ -84,14 +69,15 @@ module QA private - def resource_web_url(resource) - resource.fetch(:web_url) do - raise ResourceURLMissingError, "API resource for #{self.class.name} does not expose a `web_url` property: `#{resource}`." - end - end - + # rubocop:disable Gitlab/ModuleWithInstanceVariables def api_get - process_api_response(parse_body(api_get_from(api_get_path))) + process_api_response(parse_body(api_get_from(api_get_path))).tap do + # Record method that was used to create certain resource + # :get - resource already existed in GitLab instance and was fetched via get request + # :post - resource was created from scratch using post request + # :put - resource was created from scratch using put request + @api_fabrication_http_method ||= :get + end end def api_get_from(get_path) @@ -100,27 +86,24 @@ module QA response = get(request.url) if response.code == HTTP_STATUS_SERVER_ERROR - raise InternalServerError, "Failed to GET #{request.mask_url} - (#{response.code}): `#{response}`.\n#{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])}" + raise(InternalServerError, <<~MSG.strip) + Failed to GET #{request.mask_url} - (#{response.code}): `#{response}`. + #{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])} + MSG elsif response.code != HTTP_STATUS_OK - raise ResourceNotFoundError, "Resource at #{request.mask_url} could not be found (#{response.code}): `#{response}`.\n#{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])}" + raise(ResourceNotFoundError, <<~MSG.strip) + Resource at #{request.mask_url} could not be found (#{response.code}): `#{response}`. + #{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])} + MSG end - @api_fabrication_http_method ||= :get # rubocop:disable Gitlab/ModuleWithInstanceVariables - response end - # Query parameters formatted as `?key1=value1&key2=value2...` - # - # @return [String] - def query_parameters_to_string - query_parameters.each_with_object([]) do |(k, v), arr| - arr << "#{k}=#{v}" - end.join('&').prepend('?').chomp('?') # prepend `?` unless the string is blank - end - def api_post - process_api_response(api_post_to(api_post_path, api_post_body)) + process_api_response(api_post_to(api_post_path, api_post_body)).tap do + @api_fabrication_http_method ||= :post + end end def api_post_to(post_path, post_body, args = {}) @@ -131,7 +114,7 @@ module QA body = flatten_hash(parse_body(graphql_response)) unless graphql_response.code == HTTP_STATUS_OK && (body[:errors].nil? || body[:errors].empty?) - raise(ResourceFabricationFailedError, <<~MSG) + raise(ResourceFabricationFailedError, <<~MSG.strip) Fabrication of #{self.class.name} using the API failed (#{graphql_response.code}) with `#{graphql_response}`. #{QA::Support::Loglinking.failure_metadata(graphql_response.headers[:x_request_id])} MSG @@ -144,36 +127,64 @@ module QA response = post(Runtime::API::Request.new(api_client, post_path).url, post_body, args) unless response.code == HTTP_STATUS_CREATED - raise( - ResourceFabricationFailedError, - "Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`.\n#{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])}" - ) + raise(ResourceFabricationFailedError, <<~MSG.strip) + Fabrication of #{self.class.name} using the API failed (#{response.code}) with `#{response}`. + #{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])} + MSG end parse_body(response) end end - def flatten_hash(param) - param.each_pair.reduce({}) do |a, (k, v)| - v.is_a?(Hash) ? a.merge(flatten_hash(v)) : a.merge(k.to_sym => v) + def api_put + process_api_response(api_put_to(api_put_path, api_put_body)).tap do + @api_fabrication_http_method ||= :put end end + def api_put_to(put_path, body) + response = put(Runtime::API::Request.new(api_client, put_path).url, body) + + unless response.code == HTTP_STATUS_OK + raise(ResourceFabricationFailedError, <<~MSG.strip) + Updating #{self.class.name} using the API failed (#{response.code}) with `#{response}`. + #{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])} + MSG + end + + parse_body(response) + end + def api_delete request = Runtime::API::Request.new(api_client, api_delete_path) response = delete(request.url) unless [HTTP_STATUS_NO_CONTENT, HTTP_STATUS_ACCEPTED].include? response.code - raise ResourceNotDeletedError, "Resource at #{request.mask_url} could not be deleted (#{response.code}): `#{response}`.\n#{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])}" + raise(ResourceNotDeletedError, <<~MSG.strip) + Resource at #{request.mask_url} could not be deleted (#{response.code}): `#{response}`. + #{QA::Support::Loglinking.failure_metadata(response.headers[:x_request_id])} + MSG end response end + def resource_web_url(resource) + resource.fetch(:web_url) do + raise ResourceURLMissingError, + "API resource for #{self.class.name} does not expose a `web_url` property: `#{resource}`." + end + end + def api_client - @api_client ||= Runtime::API::Client.new(:gitlab, is_new_session: !current_url.start_with?('http'), user: api_user) + @api_client ||= Runtime::API::Client.new( + :gitlab, + is_new_session: !current_url.start_with?('http'), + user: api_user + ) end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def process_api_response(parsed_response) self.api_response = parsed_response @@ -191,6 +202,21 @@ module QA def request_url(path, **opts) Runtime::API::Request.new(api_client, path, **opts).url end + + # Query parameters formatted as `?key1=value1&key2=value2...` + # + # @return [String] + def query_parameters_to_string + query_parameters.each_with_object([]) do |(k, v), arr| + arr << "#{k}=#{v}" + end.join('&').prepend('?').chomp('?') # prepend `?` unless the string is blank + end + + def flatten_hash(param) + param.each_pair.reduce({}) do |a, (k, v)| + v.is_a?(Hash) ? a.merge(flatten_hash(v)) : a.merge(k.to_sym => v) + end + end end end end diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb index 2abe1904c92..6c03f45bdfd 100644 --- a/qa/qa/resource/base.rb +++ b/qa/qa/resource/base.rb @@ -45,7 +45,7 @@ module QA resource = options.fetch(:resource) { new } parents = options.fetch(:parents) { [] } - do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do + do_fabricate!(resource: resource, prepare_block: prepare_block) do log_and_record_fabrication(:browser_ui, resource, parents, args) { resource.fabricate!(*args) } current_url @@ -61,7 +61,7 @@ module QA resource.eager_load_api_client! - do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do + do_fabricate!(resource: resource, prepare_block: prepare_block) do log_and_record_fabrication(:api, resource, parents, args) { resource.fabricate_via_api! } end end @@ -73,14 +73,14 @@ module QA resource.eager_load_api_client! - do_fabricate!(resource: resource, prepare_block: prepare_block, parents: parents) do + do_fabricate!(resource: resource, prepare_block: prepare_block) do log_and_record_fabrication(:api, resource, parents, args) { resource.remove_via_api! } end end private - def do_fabricate!(resource:, prepare_block:, parents: []) + def do_fabricate!(resource:, prepare_block:) prepare_block.call(resource) if prepare_block resource_web_url = yield @@ -89,17 +89,12 @@ module QA resource end - def log_and_record_fabrication(fabrication_method, resource, parents, args) + def log_and_record_fabrication(fabrication_method, resource, parents, _args) start = Time.now Support::FabricationTracker.start_fabrication result = yield.tap do fabrication_time = Time.now - start - fabrication_http_method = if resource.api_fabrication_http_method == :get || resource.retrieved_from_cache - "Retrieved" - else - "Built" - end Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time) @@ -114,7 +109,7 @@ module QA Runtime::Logger.info do msg = ["==#{'=' * parents.size}>"] - msg << "#{fabrication_http_method} a #{Rainbow(name).black.bg(:white)}" + msg << "#{fabrication_type(resource, fabrication_method)} a #{Rainbow(name).black.bg(:white)}" msg << resource.identifier msg << "as a dependency of #{parents.last}" if parents.any? msg << "via #{resource.retrieved_from_cache ? 'cache' : fabrication_method}" @@ -129,6 +124,19 @@ module QA result end + # Fetch type of fabrication, either resource was built or fetched + # + # @param [Resource] resource + # @param [Symbol] method + # @return [String] + def fabrication_type(resource, method) + return "Built" if method == :browser_ui || [:post, :put].include?(resource.api_fabrication_http_method) + return "Retrieved" if resource.api_fabrication_http_method == :get || resource.retrieved_from_cache + + Runtime::Logger.warn("Resource fabrication http method has not been set properly, assuming :get value!") + "Built" + end + # Define custom attribute # # @param [Symbol] name @@ -215,8 +223,7 @@ module QA def diff(other) return if self == other - diff_values = self.comparable.to_a - other.comparable.to_a - diff_values.to_h + (comparable.to_a - other.comparable.to_a).to_h end def identifier @@ -271,7 +278,7 @@ module QA def all_attributes @all_attributes ||= self.class.ancestors .select { |clazz| clazz <= QA::Resource::Base } - .map { |clazz| clazz.instance_variable_get(:@attribute_names) } + .map { |clazz| clazz.instance_variable_get(:@attribute_names) } # rubocop:disable Performance/FlatMap .flatten .compact end diff --git a/qa/qa/resource/ci_cd_settings.rb b/qa/qa/resource/ci_cd_settings.rb new file mode 100644 index 00000000000..8240321137b --- /dev/null +++ b/qa/qa/resource/ci_cd_settings.rb @@ -0,0 +1,47 @@ +# rubocop:todo Naming/FileName +# frozen_string_literal: true + +module QA + module Resource + class CICDSettings < QA::Resource::Base + attributes :project_path, + :inbound_job_token_scope_enabled + + attribute :mutation_id do + SecureRandom.hex(6) + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + + def api_get_path + '/graphql' + end + + alias_method :api_post_path, :api_get_path + + def api_post_body + <<~GQL + mutation { + projectCiCdSettingsUpdate(input: { + clientMutationId: "#{mutation_id}" + inboundJobTokenScopeEnabled: #{inbound_job_token_scope_enabled} + fullPath: "#{project_path}" + }) + { + ciCdSettings { + inboundJobTokenScopeEnabled + } + errors + } + } + GQL + end + end + end +end + +# rubocop:enable Naming/FileName diff --git a/qa/qa/resource/integrations/web_hook/smockerable.rb b/qa/qa/resource/integrations/web_hook/smockerable.rb new file mode 100644 index 00000000000..f1d2022477e --- /dev/null +++ b/qa/qa/resource/integrations/web_hook/smockerable.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module QA + module Resource + module Integrations + module WebHook + module Smockerable + def teardown! + Service::DockerRun::Smocker.teardown! + end + + def setup(mock = Vendor::Smocker::SmockerApi::DEFAULT_MOCK, session: nil, **event_args) + Service::DockerRun::Smocker.init(wait: 10) do |smocker| + smocker.register(mock, session: session) + + webhook = fabricate_via_api! do |hook| + hook.url = smocker.url + + event_args.each do |event, bool| + hook.send("#{event}_events=", bool) + end + + hook + end + + def smocker.events(session_id = nil) + history(session_id).map do |history_response| + history_response.request.fetch(:body, {}) + end + end + + yield(webhook, smocker) + + smocker.reset + end + end + end + end + end + end +end diff --git a/qa/qa/resource/project_web_hook.rb b/qa/qa/resource/project_web_hook.rb index 86e662932e1..c8b6c2a6332 100644 --- a/qa/qa/resource/project_web_hook.rb +++ b/qa/qa/resource/project_web_hook.rb @@ -2,7 +2,17 @@ module QA module Resource - class ProjectWebHook < Base + class ProjectWebHook < WebHookBase + extend Integrations::WebHook::Smockerable + + attributes :disabled_until, :alert_status + + attribute :project do + Project.fabricate_via_api! do |resource| + resource.name = 'project-with-webhooks' + end + end + EVENT_TRIGGERS = %i[ issues job @@ -10,24 +20,13 @@ module QA note pipeline push + releases tag_push wiki_page confidential_issues confidential_note ].freeze - attr_accessor :url, :enable_ssl - - attribute :disabled_until - attribute :id - attribute :alert_status - - attribute :project do - Project.fabricate_via_api! do |resource| - resource.name = 'project-with-webhooks' - end - end - EVENT_TRIGGERS.each do |trigger| attribute "#{trigger}_events".to_sym do false @@ -35,18 +34,13 @@ module QA end def initialize - @id = nil - @enable_ssl = false - @alert_status = nil - @url = nil - end + super - def fabricate_via_api! - resource_web_url = super - - @id = api_response[:id] + @push_events_branch_filter = [] + end - resource_web_url + def add_push_event_branch_filter(branch) + @push_events_branch_filter << branch end def resource_web_url(resource) @@ -65,7 +59,9 @@ module QA body = { id: project.id, url: url, - enable_ssl_verification: enable_ssl + enable_ssl_verification: enable_ssl_verification, + token: token, + push_events_branch_filter: @push_events_branch_filter.join(',') } EVENT_TRIGGERS.each_with_object(body) do |trigger, memo| attr = "#{trigger}_events" diff --git a/qa/qa/resource/runner_base.rb b/qa/qa/resource/runner_base.rb index 399d1153dc2..9e38ba9ab64 100644 --- a/qa/qa/resource/runner_base.rb +++ b/qa/qa/resource/runner_base.rb @@ -35,7 +35,6 @@ module QA @config = nil @run_untagged = nil @name = "qa-runner-#{SecureRandom.hex(4)}" - @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine-v15.8.3' @executor = :shell @executor_image = 'registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.7' end @@ -95,6 +94,8 @@ module QA def start_container_and_register @docker_container ||= Service::DockerRun::GitlabRunner.new(name).tap do |runner| + runner.image = image if image + Support::Retrier.retry_on_exception(sleep_interval: 5) do runner.pull end @@ -102,7 +103,6 @@ module QA runner.token = token runner.address = Runtime::Scenario.gitlab_address runner.tags = tags if tags - runner.image = image runner.config = config if config runner.executor = executor runner.executor_image = executor_image if executor == :docker diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb index a79e8c7de6b..84711075442 100644 --- a/qa/qa/resource/snippet.rb +++ b/qa/qa/resource/snippet.rb @@ -22,9 +22,7 @@ module QA end def fabricate! - Page::Main::Menu.perform do |menu| - menu.go_to_menu_dropdown_option(:snippets_link) - end + Page::Main::Menu.perform(&:go_to_snippets) Page::Dashboard::Snippet::Index.perform(&:go_to_new_snippet_page) diff --git a/qa/qa/resource/web_hook_base.rb b/qa/qa/resource/web_hook_base.rb new file mode 100644 index 00000000000..d7469466212 --- /dev/null +++ b/qa/qa/resource/web_hook_base.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module QA + module Resource + class WebHookBase < Base + attributes :id, :url + + attribute :token do + nil + end + + attribute :enable_ssl_verification do + false + end + + def fabricate_via_api! + resource_web_url = super + + @id = api_response[:id] + + resource_web_url + end + + # @return [String] the api path to fetch the resource + def api_get_path + raise NotImplementedError, not_implemented_message(__callee__) + end + + # @return [String] the api path to create the resource + def api_post_path + raise NotImplementedError, not_implemented_message(__callee__) + end + + # @return [Hash] the payload needed to create the resource + def api_post_body + raise NotImplementedError, not_implemented_message(__callee__) + end + + private + + def not_implemented_message(callee) + "#{self.class} must implement ##{callee}" + end + end + end +end diff --git a/qa/qa/runtime/allure_report.rb b/qa/qa/runtime/allure_report.rb index a9152a5555c..e726f7a316f 100644 --- a/qa/qa/runtime/allure_report.rb +++ b/qa/qa/runtime/allure_report.rb @@ -25,11 +25,8 @@ module QA # # @return [void] def configure_allure - # Match job names like ee:relative, ce:update etc. and set as execution environment - env_matcher = /^(?<env>\w{2}:\S+)/ - AllureRspec.configure do |config| - config.results_directory = 'tmp/allure-results' + config.results_directory = ENV['QA_ALLURE_RESULTS_DIRECTORY'] || 'tmp/allure-results' config.clean_results_directory = true # automatically attach links to testcases and issues @@ -38,11 +35,11 @@ module QA config.issue_tag = :issue config.link_issue_pattern = '{}' - config.environment_properties = environment_info if Env.running_in_ci? - - # Set custom environment name to separate same specs executed on different environments - if Env.running_in_ci? && Env.ci_job_name.match?(env_matcher) - config.environment = Env.ci_job_name.match(env_matcher).named_captures['env'] + if Env.running_in_ci? + config.environment_properties = environment_info + # Set custom environment name to separate same specs executed in different jobs + # Drop number postfixes from parallel jobs by only matching non whitespace characters + config.environment = Env.ci_job_name.match(/^\S+/)[0] end end end @@ -77,7 +74,7 @@ module QA config.add_formatter(QA::Support::Formatters::AllureMetadataFormatter) config.add_formatter(AllureRspecFormatter) - config.append_after do |example| + config.append_after do Allure.add_attachment( name: 'browser.log', source: Capybara.current_session.driver.browser.logs.get(:browser).map(&:to_s).join("\n\n"), @@ -92,7 +89,7 @@ module QA # # @return [Hash] def environment_info - lambda do + -> do return {} unless Env.admin_personal_access_token || Env.personal_access_token client = Env.admin_personal_access_token ? API::Client.as_admin : API::Client.new diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index f01657c8deb..945823fb9c0 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -172,6 +172,7 @@ module QA } if QA::Runtime::Env.remote_grid + selenium_options[:browser] = :remote selenium_options[:url] = QA::Runtime::Env.remote_grid capabilities[:browserVersion] = 'latest' capabilities['sauce:options'] = { tunnelIdentifier: QA::Runtime::Env.remote_tunnel_id } diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index b53c2320537..810912c7ccf 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -376,13 +376,13 @@ module QA # Specifies the token that can be used for the GitHub API def github_access_token - ENV['GITHUB_ACCESS_TOKEN'].to_s.strip + ENV['QA_GITHUB_ACCESS_TOKEN'].to_s.strip end def require_github_access_token! return unless github_access_token.empty? - raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN" + raise ArgumentError, "Please provide QA_GITHUB_ACCESS_TOKEN" end def require_admin_access_token! @@ -463,6 +463,16 @@ module QA enabled?(ENV['QA_SAVE_TEST_METRICS'], default: false) end + def ee_license + return ENV["QA_EE_LICENSE"] if ENV["QA_EE_LICENSE"] + + ENV["EE_LICENSE"].tap do |license| + next unless license + + Runtime::Logger.warn("EE_LICENSE environment variable is deprecated, please use QA_EE_LICENSE instead!") + end + end + def ee_activation_code ENV['QA_EE_ACTIVATION_CODE'] end @@ -514,6 +524,10 @@ module QA ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir end + def super_sidebar_enabled? + enabled?(ENV['QA_SUPER_SIDEBAR_ENABLED'], default: false) + end + def require_slack_env! missing_env = %i[slack_workspace slack_email slack_password].select do |method| ::QA::Runtime::Env.public_send(method).nil? diff --git a/qa/qa/service/cluster_provider/gcloud.rb b/qa/qa/service/cluster_provider/gcloud.rb index 749ebca8897..d33ae4915b5 100644 --- a/qa/qa/service/cluster_provider/gcloud.rb +++ b/qa/qa/service/cluster_provider/gcloud.rb @@ -33,8 +33,7 @@ module QA delete_cluster end - # kas is hardcoded to staging since this test should only run in staging for now - def install_kubernetes_agent(agent_token) + def install_kubernetes_agent(agent_token:, kas_address:) install_helm shell <<~CMD.tr("\n", ' ') @@ -45,7 +44,8 @@ module QA --create-namespace --set image.tag=#{Runtime::Env.gitlab_agentk_version} --set config.token=#{agent_token} - --set config.kasAddress=wss://kas.staging.gitlab.com + --set config.kasAddress=#{kas_address} + --set config.kasHeaders="{Cookie: gitlab_canary=#{target_canary?}}" CMD end @@ -59,6 +59,10 @@ module QA CMD end + def target_canary? + Runtime::Env.qa_cookies.to_s.include?("gitlab_canary=true") + end + def login_if_not_already_logged_in if Runtime::Env.has_gcloud_credentials? attempt_login_with_env_vars diff --git a/qa/qa/service/docker_run/gitlab_runner.rb b/qa/qa/service/docker_run/gitlab_runner.rb index a8fcf8f9332..d40517ae535 100644 --- a/qa/qa/service/docker_run/gitlab_runner.rb +++ b/qa/qa/service/docker_run/gitlab_runner.rb @@ -16,7 +16,7 @@ module QA MSG def initialize(name) - @image = 'gitlab/gitlab-runner:alpine-v15.8.3' + @image = 'registry.gitlab.com/gitlab-org/gitlab-runner:alpine' @name = name || "qa-runner-#{SecureRandom.hex(4)}" @run_untagged = true @executor = :shell diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb index 5362124bee5..ed57d825643 100644 --- a/qa/qa/service/kubernetes_cluster.rb +++ b/qa/qa/service/kubernetes_cluster.rb @@ -5,6 +5,7 @@ require 'mkmf' module QA module Service class KubernetesCluster + include Support::API include Service::Shellout attr_reader :api_url, :ca_certificate, :token, :rbac, :provider @@ -36,7 +37,7 @@ module QA end def install_kubernetes_agent(agent_token) - @provider.install_kubernetes_agent(agent_token) + @provider.install_kubernetes_agent(agent_token: agent_token, kas_address: fetch_kas_address) end def create_secret(secret, secret_name) @@ -73,6 +74,17 @@ module QA `kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'` end + def fetch_kas_address + api_client = Runtime::API::Client.new(:gitlab) + + Support::Retrier.retry_until do + response = get(Runtime::API::Request.new(api_client, '/metadata').url) + body = parse_body(response) + + body.dig(:kas, :externalUrl) || raise("Failed to fetch KAS address from #{body}") + end + end + def fetch_credentials return global_credentials unless rbac diff --git a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb index 9017aba8e4a..407e479bda2 100644 --- a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb @@ -1,12 +1,7 @@ # frozen_string_literal: true module QA - # https://github.com/gitlab-qa-github/import-test <- project under test - # - RSpec.describe 'Manage', product_group: :import, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391228', - type: :waiting_on - } do + RSpec.describe 'Manage', product_group: :import do describe 'GitHub import' do include_context 'with github import' diff --git a/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb index 8439b881ed7..a6cdd737341 100644 --- a/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb +++ b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb @@ -2,78 +2,57 @@ module QA RSpec.describe 'Manage' do - describe 'WebHooks integration', :requires_admin, :integrations, :orchestrated, product_group: :integrations do + describe( + 'WebHooks integration', + :requires_admin, + :integrations, + :orchestrated, + product_group: :integrations + ) do before(:context) do toggle_local_requests(true) end after(:context) do - Service::DockerRun::Smocker.teardown! + Resource::ProjectWebHook.teardown! end let(:session) { SecureRandom.hex(5) } let(:tag_name) { SecureRandom.hex(5) } it 'sends a push event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348945' do - setup_webhook(push: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(session: session, push: true) do |webhook, smocker| Resource::Repository::ProjectPush.fabricate! do |project_push| project_push.project = webhook.project end - wait_until do - !smocker.history(session).empty? - end - - events = smocker.history(session).map(&:as_hook_event) - aggregate_failures do - expect(events.size).to be(1), "Should have 1 event: \n#{events.map(&:raw).join("\n")}" - expect(events[0].project_name).to eql(webhook.project.name) - expect(events[0].push?).to be(true), "Not push event: \n#{events[0].raw}" - end + expect_web_hook_single_event_success(webhook, smocker, type: 'push') end end it 'sends a merge request event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349720' do - setup_webhook(merge_requests: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(session: session, merge_requests: true) do |webhook, smocker| Resource::MergeRequest.fabricate_via_api! do |merge_request| merge_request.project = webhook.project end - wait_until do - !smocker.history(session).empty? - end - - events = smocker.history(session).map(&:as_hook_event) - aggregate_failures do - expect(events.size).to be(1), "Should have 1 event: \n#{events.map(&:raw).join("\n")}" - expect(events[0].project_name).to eql(webhook.project.name) - expect(events[0].mr?).to be(true), "Not MR event: \n#{events[0].raw}" - end + expect_web_hook_single_event_success(webhook, smocker, type: 'merge_request') end end it 'sends a wiki page event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349722' do - setup_webhook(wiki_page: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(session: session, wiki_page: true) do |webhook, smocker| Resource::Wiki::ProjectPage.fabricate_via_api! do |page| page.project = webhook.project end - wait_until do - !smocker.history(session).empty? - end - - events = smocker.history(session).map(&:as_hook_event) - aggregate_failures do - expect(events.size).to be(1), "Should have 1 event: \n#{events.map(&:raw).join("\n")}" - expect(events[0].project_name).to eql(webhook.project.name) - expect(events[0].wiki?).to be(true), "Not wiki event: \n#{events[0].raw}" - end + expect_web_hook_single_event_success(webhook, smocker, type: 'wiki_page') end end it 'sends an issues and note event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349723' do - setup_webhook(issues: true, note: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(session: session, issues: true, note: true) do |webhook, smocker| issue = Resource::Issue.fabricate_via_api! do |issue_init| issue_init.project = webhook.project end @@ -83,25 +62,24 @@ module QA note.issue = issue end - wait_until do - smocker.history(session).size > 1 - end + expect { smocker.events(session).size }.to eventually_eq(2) + .within(max_duration: 30, sleep_interval: 2), + -> { "Should have 2 events, got: #{smocker.stringified_history(session)}" } - events = smocker.history(session).map(&:as_hook_event) - aggregate_failures do - issue_event = events.find(&:issue?) - note_event = events.find(&:note?) + events = smocker.events(session) - expect(events.size).to be(2), "Should have 2 events: \n#{events.map(&:raw).join("\n")}" - expect(issue_event).not_to be(nil), "Not issue event: \n#{events[0].raw}" - expect(note_event).not_to be(nil), "Not note event: \n#{events[1].raw}" + aggregate_failures do + expect(events).to include( + a_hash_including(object_kind: 'note'), + a_hash_including(object_kind: 'issue') + ) end end end it 'sends a tag event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/383577' do - setup_webhook(tag_push: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(session: session, tag_push: true) do |webhook, smocker| project_push = Resource::Repository::ProjectPush.fabricate! do |project_push| project_push.project = webhook.project end @@ -112,16 +90,7 @@ module QA tag.name = tag_name end - wait_until do - smocker.history(session).size == 1 - end - - events = smocker.history(session).map(&:as_hook_event) - aggregate_failures do - expect(events.size).to be(1), "Should have 1 event: \n#{events.map(&:raw).join("\n")}" - expect(events[0].project_name).to eql(webhook.project.name) - expect(events[0].tag?).to be(true), "Not tag event: \n#{events[0].raw}" - end + expect_web_hook_single_event_success(webhook, smocker, type: 'tag_push') end end @@ -144,16 +113,19 @@ module QA it 'hook is auto-disabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/389595' do - setup_webhook(fail_mock, issues: true) do |webhook, smocker| + Resource::ProjectWebHook.setup(fail_mock, session: session, issues: true) do |webhook, smocker| hook_trigger_times.times do Resource::Issue.fabricate_via_api! do |issue_init| issue_init.project = webhook.project end + + # using sleep to give rate limiter a chance to activate. + sleep 0.5 end - expect { smocker.history(session).size }.to eventually_eq(disabled_after) + expect { smocker.events(session).size }.to eventually_eq(disabled_after) .within(max_duration: 30, sleep_interval: 2), - -> { "Should have #{disabled_after} events, got: #{smocker.history(session).size}" } + -> { "Should have #{disabled_after} events, got: #{smocker.events(session).size}" } webhook.reload! @@ -161,34 +133,27 @@ module QA end end end + end - private - - def setup_webhook(mock = Vendor::Smocker::SmockerApi::DEFAULT_MOCK, **event_args) - Service::DockerRun::Smocker.init(wait: 10) do |smocker| - smocker.register(mock, session: session) - - webhook = Resource::ProjectWebHook.fabricate_via_api! do |hook| - hook.url = smocker.url - - event_args.each do |event, bool| - hook.send("#{event}_events=", bool) - end - end + private - yield(webhook, smocker) + def expect_web_hook_single_event_success(webhook, smocker, type:) + expect { smocker.events(session).size }.to eventually_eq(1) + .within(max_duration: 30, sleep_interval: 2), + -> { "Should have 1 events, got: #{smocker.stringified_history(session)}" } - smocker.reset - end - end + event = smocker.events(session).first - def toggle_local_requests(on) - Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: on) + aggregate_failures do + expect(event).to match(a_hash_including( + object_kind: type, + project: a_hash_including(name: webhook.project.name) + )) end + end - def wait_until(timeout = 120, &block) - Support::Waiter.wait_until(max_duration: timeout, reload_page: false, raise_on_failure: false, &block) - end + def toggle_local_requests(on) + Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: on) end end end diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb index 2dcbbadb4aa..bc057f948a8 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb @@ -15,7 +15,7 @@ module QA let(:source_issue_comments) do source_issue.comments.map do |note| - { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) } + { **note.except(:id, :noteable_id, :project_id), author: note[:author].except(:web_url) } end end @@ -32,7 +32,7 @@ module QA let(:imported_issue_comments) do imported_issue.comments.map do |note| - { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) } + { **note.except(:id, :noteable_id, :project_id), author: note[:author].except(:web_url) } end end @@ -67,11 +67,7 @@ module QA it( 'preserves related merge request', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386305', - quarantine: { - type: :bug, - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/386308' - } + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/386305' ) do expect_project_import_finished_successfully expect(imported_related_mrs).to eq([source_mr.iid]) diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb index 9ce028318c3..d01adb5d5b4 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb @@ -65,10 +65,6 @@ module QA let(:mrs) { fetch_mrs(imported_project, api_client) } let(:issues) { fetch_issues(imported_project, api_client) } - before do - Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects) - end - # rubocop:disable RSpec/InstanceVariable after do |example| next unless defined?(@import_time) diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb index 127db36052f..8c20c2cc0e2 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb @@ -37,7 +37,7 @@ module QA let!(:source_mr_approvers) { [source_admin_user.email] } let(:source_mr_comments) do source_mr.comments.map do |note| - { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) } + { **note.except(:id, :noteable_id, :project_id), author: note[:author].except(:web_url) } end end @@ -52,11 +52,11 @@ module QA let(:imported_mr_comments) do imported_mr.comments.map do |note| - { **note.except(:id, :noteable_id), author: note[:author].except(:web_url) } + { **note.except(:id, :noteable_id, :project_id), author: note[:author].except(:web_url) } end end - let(:imported_mr_reviewers) { imported_mr.reviewers.map { |reviewer| reviewer[:username] } } + let(:imported_mr_reviewers) { imported_mr.reviewers.pluck(:username) } let(:imported_mr_approvers) do imported_mr.approval_configuration[:approved_by].map { |usr| usr.dig(:user, :username) } end diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb index 60ece89844d..43701a6b740 100644 --- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb +++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb @@ -6,7 +6,7 @@ module QA include_context 'with gitlab project migration' # this spec is used as a sanity test for gitlab migration because it can run outside of orchestrated setup - context 'with import within same instance', orchestrated: false, import: false do + context 'with import within same instance', :reliable, orchestrated: false, import: false do let!(:source_project_with_readme) { true } let!(:source_gitlab_address) { Runtime::Scenario.gitlab_address } let!(:source_admin_api_client) { admin_api_client } diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb index 124b6c9cd44..c50eb2f4fdf 100644 --- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb +++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb @@ -79,19 +79,24 @@ module QA 'is allowed to commit to sub-group project via the API', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349' ) do - expect do - Resource::Repository::Commit.fabricate_via_api! do |commit| - commit.api_client = parent_group_user_api_client - commit.project = sub_group_project - commit.branch = "new_branch_#{SecureRandom.hex(8)}" - commit.start_branch = sub_group_project.default_branch - commit.commit_message = 'Add new file' - commit.add_files([{ file_path: 'test.txt', content: 'new file' }]) - end - rescue StandardError => e - QA::Runtime::Logger.error("Full failure message: #{e.message}") - raise - end.not_to raise_error + # Retry is needed due to delays with project authorization updates + # Long term solution to accessing the status of a project authorization update + # has been proposed in https://gitlab.com/gitlab-org/gitlab/-/issues/393369 + QA::Support::Retrier.retry_on_exception(max_attempts: 5, sleep_interval: 2) do + expect do + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.api_client = parent_group_user_api_client + commit.project = sub_group_project + commit.branch = "new_branch_#{SecureRandom.hex(8)}" + commit.start_branch = sub_group_project.default_branch + commit.commit_message = 'Add new file' + commit.add_files([{ file_path: 'test.txt', content: 'new file' }]) + end + rescue StandardError => e + QA::Runtime::Logger.error("Full failure message: #{e.message}") + raise + end.not_to raise_error + end end after do diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb index 71bd03fab17..7e329371745 100644 --- a/qa/qa/specs/features/api/3_create/repository/files_spec.rb +++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb @@ -99,7 +99,6 @@ module QA # expect(response.headers[:cache_control]).to include("no-store") expect(response.headers[:cache_control]).to include("no-cache") - expect(response.headers[:pragma]).to eq("no-cache") expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT") expect(response.headers[:content_disposition]).to include("attachment") expect(response.headers[:content_disposition]).not_to include("inline") diff --git a/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb b/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb index 8890b3ff317..c66bd16afe9 100644 --- a/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb +++ b/qa/qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Pipeline API defined variable inheritance' do include_context 'variable inheritance test prep' diff --git a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb index bd0ec13b1f8..2d9deec399c 100644 --- a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb +++ b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Pipeline with project file variables' do let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } diff --git a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb index 461928cbf1f..b5a8df15ddc 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/import/import_github_repo_spec.rb @@ -1,10 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Manage', product_group: :import, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391230', - type: :waiting_on - } do + RSpec.describe 'Manage', product_group: :import do describe 'GitHub import' do include_context 'with github import' diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb index 94b383a746d..ac08ecec786 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb @@ -82,7 +82,7 @@ module QA Page::Main::Menu.perform(&:click_user_profile_link) Page::User::Show.perform do |show| - show.click_following_link + show.click_following_tab show.click_user_link(followed_user.username) aggregate_failures do 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 236af93716f..349fa054ff0 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 @@ -17,11 +17,9 @@ module QA settings.enable_ff_only end - Resource::Repository::ProjectPush.fabricate! do |push| - push.project = merge_request.project - push.file_name = "other.txt" - push.file_content = "New file added!" - push.new_branch = false + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = merge_request.project + commit.add_files([{ file_path: 'other.txt', content: 'New file added!' }]) end merge_request.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb index feb0f28763c..2b04ede25b0 100644 --- a/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/project_wiki/project_based_content_creation_spec.rb @@ -15,7 +15,7 @@ module QA end it 'by adding a home page to the wiki', -testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do project.visit! Page::Project::Menu.perform(&:click_wiki) @@ -36,7 +36,7 @@ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do end it 'by adding a second page to the wiki', -testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do wiki.visit! Page::Project::Wiki::Show.perform(&:click_new_page) @@ -56,7 +56,7 @@ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347808' do end it 'by adding a home page to the wiki using git push', -testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347806' do + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347806' do empty_wiki = Resource::Wiki::ProjectPage.new do |empty_wiki| empty_wiki.project = project end @@ -76,7 +76,7 @@ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347806' do end it 'by adding a second page to the wiki using git push', -testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347807' do + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347807' do Resource::Repository::WikiPush.fabricate! do |push| push.file_name = "#{new_wiki_title}.md" push.file_content = new_wiki_content diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb index 815a8696ff7..b8a018552c6 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create', product_group: :source_code, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/352525', - type: :test_environment, - only: { job: 'review-qa-*' } - } do + RSpec.describe 'Create', product_group: :source_code do describe 'Push mirror a repository over HTTP' do it 'configures and syncs LFS objects for a (push) mirrored repository', :aggregate_failures, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347847' do Runtime::Browser.visit(:gitlab, Page::Main::Login) diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb index 63e9fdbb881..a63c5cefff4 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb @@ -52,9 +52,7 @@ module QA shared_examples 'displaying details on index page' do |snippet_type, testcase| it "shows correct details of #{snippet_type} including file number", testcase: testcase do send(snippet_type) - Page::Main::Menu.perform do |menu| - menu.go_to_menu_dropdown_option(:snippets_link) - end + Page::Main::Menu.perform(&:go_to_snippets) Page::Dashboard::Snippet::Index.perform do |snippet| aggregate_failures 'file content verification' do diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb index 6cbbfb9e7e5..cf1e4700863 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb @@ -1,12 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, - product_group: :editor, - quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387033', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Web IDE file templates' do include Runtime::Fixtures @@ -16,11 +12,6 @@ module QA project.description = 'Add file templates via the Web IDE' project.initialize_with_readme = true end - Runtime::Feature.disable(:vscode_web_ide) - end - - after(:all) do - Runtime::Feature.enable(:vscode_web_ide) end templates = [ diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb index ded1b1c9d7c..e5e3941e0cd 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387029', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Add a directory in Web IDE' do let(:project) do Resource::Project.fabricate_via_api! do |project| @@ -14,15 +12,10 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in project.visit! end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - context 'when a directory with the same name already exists' do let(:directory_name) { 'first_directory' } diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb index 45499ea1999..58afdfe7cd1 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/create_first_file_in_web_ide_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387723', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'First file using Web IDE' do let(:project) do Resource::Project.fabricate_via_api! do |project| @@ -16,14 +14,9 @@ module QA let(:file_name) { 'the very first file.txt' } before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - it "creates the first file in an empty project via Web IDE", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347803' do project.visit! Page::Project::Show.perform(&:create_first_new_file!) diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb index 3ea87d90c2d..9c40a3abe52 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/link_to_line_in_web_ide_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387035', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Link to line in Web IDE' do let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) } let(:project) do @@ -14,12 +12,10 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in end after do - Runtime::Feature.enable(:vscode_web_ide) project.remove_via_api! end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb deleted file mode 100644 index 7195dd5c970..00000000000 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_fork_in_web_ide_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor do - describe 'Open a fork in Web IDE', - skip: { - issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/351696", - type: :flaky - } do - let(:parent_project) do - Resource::Project.fabricate_via_api! do |project| - project.name = 'parent-project' - project.initialize_with_readme = true - end - end - - before do - Runtime::Feature.disable(:vscode_web_ide) - end - - after do - Runtime::Feature.enable(:vscode_web_ide) - end - - context 'when a user does not have permissions to commit to the project' do - let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) } - - context 'when no fork is present' do - it 'suggests to create a fork when a user clicks Web IDE in the main project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347823' do - Flow::Login.sign_in(as: user) - - parent_project.visit! - Page::Project::Show.perform(&:open_web_ide!) - - Page::Project::WebIDE::Edit.perform(&:fork_project!) - - submit_merge_request_upstream - end - end - - context 'when a fork is already created' do - let(:fork_project) do - Resource::Fork.fabricate_via_api! do |fork| - fork.user = user - fork.upstream = parent_project - end - end - - it 'opens the fork when a user clicks Web IDE in the main project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347824' do - Flow::Login.sign_in(as: user) - fork_project.upstream.visit! - Page::Project::Show.perform do |project_page| - expect(project_page).to have_edit_fork_button - - project_page.open_web_ide! - end - - submit_merge_request_upstream - end - - after do - fork_project.project.remove_via_api! - end - end - - def submit_merge_request_upstream - Page::Project::WebIDE::Edit.perform do |ide| - ide.wait_until_ide_loads - expect(ide).to have_project_path("#{user.username}/#{parent_project.name}") - - ide.add_file('new file', 'some random text') - ide.commit_changes(open_merge_request: true) - end - - Page::MergeRequest::New.perform(&:create_merge_request) - - parent_project.visit! - Page::Project::Menu.perform(&:click_merge_requests) - expect(page).to have_content('Update new file') - end - end - end - end -end diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb index 02d2710656d..bbfc3ba8ccd 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387031', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Open Web IDE from Diff Tab' do files = [ { @@ -47,15 +45,10 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in merge_request.visit! end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - it 'opens and edits a multi-file merge request in Web IDE from Diff Tab', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347724' do Page::MergeRequest::Show.perform do |show| show.click_diffs_tab diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb index 4c21581781d..05c58b66b09 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/review_merge_request_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', feature_flag: { name: 'vscode_web_ide', scope: :global }, product_group: :editor, quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387043', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Review a merge request in Web IDE' do let(:new_file) { 'awesome_new_file.txt' } let(:original_text) { 'Text' } @@ -26,15 +24,10 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in merge_request.visit! end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - it 'opens and edits a merge request in Web IDE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347786' do Page::MergeRequest::Show.perform do |show| show.click_open_in_web_ide diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb index 080832990c9..8082c54a6ee 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/server_hooks_custom_error_message_spec.rb @@ -1,13 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' }, - feature_flag: { name: 'vscode_web_ide', scope: :global }, - product_group: :editor, - quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387928', - type: :stale - } do + RSpec.describe 'Create', :skip_live_env, except: { job: 'review-qa-*' }, product_group: :editor do describe 'Git Server Hooks' do let(:file_path) { File.join(Runtime::Path.fixtures_path, 'web_ide', 'README.md') } @@ -20,15 +15,10 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in project.visit! end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - context 'with custom error messages' do it 'renders preconfigured error message when user hook failed on commit in WebIDE', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/364751' do diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb index b83a95694de..abc7c37a1d4 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb @@ -1,9 +1,8 @@ # frozen_string_literal: true +# TODO: remove this test when 'vscode_web_ide' feature flag is default enabled module QA - RSpec.describe 'Create', product_group: :editor, - feature_flag: { name: 'vscode_web_ide', scope: :global }, - quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/387032', type: :stale } do + RSpec.describe 'Create', :skip_live_env, product_group: :editor do describe 'Upload a file in Web IDE' do let(:file_path) { File.join(Runtime::Path.fixtures_path, 'web_ide', file_name) } @@ -15,17 +14,12 @@ module QA end before do - Runtime::Feature.disable(:vscode_web_ide) Flow::Login.sign_in project.visit! Page::Project::Show.perform(&:open_web_ide!) end - after do - Runtime::Feature.enable(:vscode_web_ide) - end - context 'when a file with the same name already exists' do let(:file_name) { 'README.md' } diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_parent_child_pipelines_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_parent_child_pipelines_spec.rb new file mode 100644 index 00000000000..072c957f4dc --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_parent_child_pipelines_spec.rb @@ -0,0 +1,448 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Verify', :runner, product_group: :pipeline_security, quarantine: { + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/396855', + type: :flaky + } do + describe "Unlocking job artifacts across parent-child pipelines" do + let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } + + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'unlock-job-artifacts-parent-child-project' + end + end + + let!(:runner) do + Resource::ProjectRunner.fabricate! do |runner| + runner.project = project + runner.name = executor + runner.tags = [executor] + end + end + + let(:parent_test_job_name) { 'test-job-parent' } + let(:child_test_job_name) { 'test-job-child' } + + let(:previous_successful_pipeline) do + Resource::Pipeline.fabricate_via_api! do |pipeline| + pipeline.project = project + end + end + + before do + Flow::Login.sign_in + project.visit! + end + + context 'without strategy:depend' do + let(:strategy) { nil } + + before do + add_parent_child_ci_files + Flow::Pipeline.wait_for_latest_pipeline(status: 'passed') + previous_successful_pipeline + Flow::Pipeline.wait_for_latest_pipeline(status: 'passed') + end + + context 'when latest pipeline family is successful' do + before do + update_parent_child_ci_files + end + + it 'unlocks job artifacts from previous successful pipeline family', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/395516' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + end + end + + context 'when latest parent pipeline failed' do + before do + update_failed_parent_ci_file + end + + it 'does not unlock job artifacts from previous successful pipeline family', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396243' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to be_failed + # FIXME: this should be unlocked, + # to be fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110575 + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + end + end + + context 'when latest child pipeline failed' do + before do + update_failed_child_ci_file + end + + it 'unlocks job artifacts from previous successful pipeline family because the latest parent is successful', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396244' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to be_failed + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + end + end + end + + context 'with strategy:depend' do + let(:strategy) { 'depend' } + + before do + add_parent_child_ci_files + Flow::Pipeline.wait_for_latest_pipeline(status: 'passed') + previous_successful_pipeline + Flow::Pipeline.wait_for_latest_pipeline(status: 'passed') + end + + context 'when latest pipeline family is successful' do + before do + update_parent_child_ci_files + end + + it 'unlocks job artifacts from previous successful pipeline family', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396245' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + end + end + + context 'when latest parent pipeline failed' do + before do + update_failed_parent_ci_file + end + + it 'does not unlock job artifacts from previous successful pipeline family', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396246' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + # FIXME: this should be unlocked, + # to be fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110575 + expect(job).to be_failed + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + end + end + + context 'when latest child pipeline failed' do + before do + update_failed_child_ci_file + end + + it 'does not unlock job artifacts from previous successful pipeline family', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396248' do + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful + # FIXME: this should be unlocked, + # to be fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110575 + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to be_failed + # FIXME: this should be unlocked, + # to be fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110575 + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Flow::Pipeline.visit_pipeline_job_page(job_name: parent_test_job_name) + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + previous_successful_pipeline.visit! + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.expand_child_pipeline + pipeline.click_job(child_test_job_name) + end + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + end + end + end + + private + + def update_parent_child_ci_files + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Update parent and child pipelines CI files.' + commit.update_files( + [ + parent_ci_file, + child_ci_file + ] + ) + end + end + + def update_failed_parent_ci_file + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Fail parent pipeline.' + commit.update_files( + [ + parent_failed_ci_file + ] + ) + end + end + + def update_failed_child_ci_file + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Fail child pipeline.' + commit.update_files( + [ + child_failed_ci_file + ] + ) + end + end + + def add_parent_child_ci_files + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add parent and child pipelines CI files.' + commit.add_files( + [ + parent_ci_file, + child_ci_file + ] + ) + end + end + + def parent_ci_file + { + file_path: '.gitlab-ci.yml', + content: <<~YAML + trigger-child: + stage: test + trigger: + include: ".child-ci.yml" + strategy: #{strategy} + + #{parent_test_job_name}: + stage: test + tags: ["#{executor}"] + script: echo "parent test" + artifacts: + paths: ['.gitlab-ci.yml'] + when: always + YAML + } + end + + def parent_failed_ci_file + { + file_path: '.gitlab-ci.yml', + content: <<~YAML + trigger-child: + stage: test + trigger: + include: ".child-ci.yml" + strategy: #{strategy} + + #{parent_test_job_name}: + stage: test + tags: ["#{executor}"] + script: echo "parent test" && exit 1 + artifacts: + paths: ['.gitlab-ci.yml'] + when: always + YAML + } + end + + def child_ci_file + { + file_path: '.child-ci.yml', + content: <<~YAML + #{child_test_job_name}: + stage: test + tags: ["#{executor}"] + script: echo "child test" + artifacts: + paths: ['.child-ci.yml'] + when: always + YAML + } + end + + def child_failed_ci_file + { + file_path: '.child-ci.yml', + content: <<~YAML + #{child_test_job_name}: + stage: test + tags: ["#{executor}"] + script: echo "child test" && exit 1 + artifacts: + paths: ['.child-ci.yml'] + when: always + YAML + } + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb new file mode 100644 index 00000000000..a954bd16386 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do + describe "Unlocking job artifacts across pipelines" do + let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } + + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'unlock-job-artifacts-project' + end + end + + let!(:runner) do + Resource::ProjectRunner.fabricate! do |runner| + runner.project = project + runner.name = executor + runner.tags = [executor] + end + end + + let(:test_job_name) { 'test-job' } + + before do + Flow::Login.sign_in + end + + context 'when latest pipeline is successful' do + it 'unlocks job artifacts from previous successful pipeline', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/394807' do + add_ci_file + project.visit! + + previous_successful_pipeline = Resource::Pipeline.fabricate! do |pipeline| + pipeline.project = project + end + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + update_ci_script('echo bye') + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + Flow::Pipeline.visit_pipeline_job_page(pipeline: previous_successful_pipeline, job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + end + end + + context 'when latest pipeline failed' do + it 'unlocks job artifacts from failed pipelines, keeps job artifacts from latest successful pipeline', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/394808', + quarantine: { + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/266958', + type: :bug + } do + add_ci_file + project.visit! + + successful_pipeline = Resource::Pipeline.fabricate! do |pipeline| + pipeline.project = project + end + + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + Flow::Pipeline.visit_pipeline_job_page(job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + + update_ci_script('echo test && exit 1') + + failed_pipeline_1 = Resource::Pipeline.fabricate! do |pipeline| + pipeline.project = project + end + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Flow::Pipeline.visit_pipeline_job_page(job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + update_ci_script('echo bye && exit 1') + project.visit! + + Flow::Pipeline.visit_latest_pipeline(status: 'failed') + Flow::Pipeline.visit_pipeline_job_page(job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + Flow::Pipeline.visit_pipeline_job_page(pipeline: failed_pipeline_1, job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_unlocked_artifact + end + + Flow::Pipeline.visit_pipeline_job_page(pipeline: successful_pipeline, job_name: test_job_name) + + Page::Project::Job::Show.perform do |job| + expect(job).to have_locked_artifact + end + end + end + + private + + def add_ci_file + script = 'echo test' + ci_file = ci_file_with_job_artifact(script) + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = "Set script #{script}" + commit.add_files([ci_file]) + end + end + + def update_ci_script(script) + ci_file = ci_file_with_job_artifact(script) + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = "Set script #{script}" + commit.update_files([ci_file]) + end + end + + def add_failing_ci_file + ci_file = ci_file_with_job_artifact('echo test && exit 1') + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add failing CI file.' + commit.add_files([ci_file]) + end + end + + def ci_file_with_job_artifact(script) + { + file_path: '.gitlab-ci.yml', + content: <<~YAML + #{test_job_name}: + stage: test + tags: ["#{executor}"] + script: #{script} + artifacts: + paths: ['.gitlab-ci.yml'] + when: always + YAML + } + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb index aec0da99a5c..8474e5c1b37 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_remove_ci_variable_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Verify' do - describe 'Add or Remove CI variable via UI', :smoke, product_group: :pipeline_authoring do + describe 'Add or Remove CI variable via UI', :smoke, product_group: :pipeline_security do let(:project) do Resource::Project.fabricate_via_api_unless_fips! do |project| project.name = 'project-with-ci-variables' diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb index 2ae28d54242..4c1319da0cb 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Pipeline with customizable variable' do let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:pipeline_job_name) { 'customizable-variable' } @@ -51,11 +51,6 @@ module QA project.visit! Page::Project::Menu.perform(&:click_ci_cd_pipelines) Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button) - - # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again. - # TODO: Investigate alternatives to deal with cache implementation - # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233 - page.refresh end after do diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb index a8ec0a1c835..e2d25e64687 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Pipeline with protected variable' do let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } let(:protected_value) { Faker::Alphanumeric.alphanumeric(number: 8) } diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb index 1878292015e..b79f8b5f1f4 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Verify' do - describe 'Pipeline with prefill variables' do + describe 'Pipeline with prefill variables', product_group: :pipeline_security do let(:prefill_variable_description1) { Faker::Lorem.sentence } let(:prefill_variable_value1) { Faker::Lorem.word } let(:prefill_variable_value5) { Faker::Lorem.word } @@ -54,11 +54,6 @@ module QA # Navigate to Run Pipeline page Page::Project::Menu.perform(&:click_ci_cd_pipelines) Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button) - - # Sometimes the variables will not be prefilled because of reactive cache so we revisit the page again. - # TODO: Investigate alternatives to deal with cache implementation - # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/381233 - page.refresh end it 'shows only variables with description as prefill variables on the run pipeline page', diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb index 81ccd36c514..15959721935 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb @@ -2,7 +2,7 @@ module QA RSpec.describe 'Verify', :runner do - describe 'Pipeline with raw variables in YAML', product_group: :pipeline_authoring do + describe 'Pipeline with raw variables in YAML', product_group: :pipeline_security do let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:pipeline_job_name) { 'rspec' } diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb index a5ebd4004d2..12c29ac2363 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'UI defined variable' do include_context 'variable inheritance test prep' diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb index f53454b801c..1d354daaa5b 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_non_inheritable_when_forward_pipeline_variables_false_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'UI defined variable' do include_context 'variable inheritance test prep' diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb new file mode 100644 index 00000000000..dc8db7ec387 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring, feature_flag: { + name: 'ci_batch_project_includes_context', + scope: :global + } do + describe 'Include multiple files from multiple projects' do + let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } + + let(:main_project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'project-with-pipeline' + end + end + + let(:project1) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'external-project-1' + end + end + + let(:project2) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'external-project-2' + end + end + + let!(:runner) do + Resource::ProjectRunner.fabricate! do |runner| + runner.project = main_project + runner.name = executor + runner.tags = [executor] + end + end + + def before_do + Flow::Login.sign_in + + add_included_files_for(main_project) + add_included_files_for(project1) + add_included_files_for(project2) + add_main_ci_file(main_project) + + main_project.visit! + Flow::Pipeline.visit_latest_pipeline(status: 'passed') + end + + after do + runner.remove_via_api! + end + + context 'when FF is on', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396374' do + before do + Runtime::Feature.enable(:ci_batch_project_includes_context, project: main_project) + sleep 60 + + before_do + end + + it 'runs the pipeline with composed config' do + Page::Project::Pipeline::Show.perform do |pipeline| + aggregate_failures 'pipeline has all expected jobs' do + expect(pipeline).to have_job('test_for_main') + expect(pipeline).to have_job("test1_for_#{project1.full_path}") + expect(pipeline).to have_job("test1_for_#{project2.full_path}") + expect(pipeline).to have_job("test2_for_#{project1.full_path}") + expect(pipeline).to have_job("test2_for_#{main_project.full_path}") + end + end + end + end + + context 'when FF is off', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/396375' do + before do + Runtime::Feature.disable(:ci_batch_project_includes_context, project: main_project) + sleep 60 + + before_do + end + + it 'runs the pipeline with composed config' do + Page::Project::Pipeline::Show.perform do |pipeline| + aggregate_failures 'pipeline has all expected jobs' do + expect(pipeline).to have_job('test_for_main') + expect(pipeline).to have_job("test1_for_#{project1.full_path}") + expect(pipeline).to have_job("test1_for_#{project2.full_path}") + expect(pipeline).to have_job("test2_for_#{project1.full_path}") + expect(pipeline).to have_job("test2_for_#{main_project.full_path}") + end + end + end + end + + private + + def add_included_files_for(project) + files = [ + { + file_path: 'file1.yml', + content: <<~YAML + test1_for_#{project.full_path}: + tags: ["#{executor}"] + script: echo hello1 + YAML + }, + { + file_path: 'file2.yml', + content: <<~YAML + test2_for_#{project.full_path}: + tags: ["#{executor}"] + script: echo hello2 + YAML + } + ] + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add files' + commit.add_files(files) + end + end + + def add_main_ci_file(project) + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add config file' + commit.add_files([main_ci_file]) + end + end + + def main_ci_file + { + file_path: '.gitlab-ci.yml', + content: <<~YAML + include: + - project: #{project1.full_path} + file: file1.yml + - project: #{project2.full_path} + file: file1.yml + - project: #{project1.full_path} + file: file2.yml + - project: #{main_project.full_path} + file: file2.yml + + test_for_main: + tags: ["#{executor}"] + script: echo hello + YAML + } + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb index 588d22275df..5543e39e38c 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/locked_artifacts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, :requires_admin, product_group: :pipeline_insights do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Artifacts' do context 'when locked' do let(:file_name) { 'artifact.txt' } diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb index 7d1339d2c2d..6295c596dda 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do describe 'Pass dotenv variables to downstream via bridge' do let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" } let(:upstream_var) { Faker::Alphanumeric.alphanumeric(number: 8) } diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb index d4fee70fbf3..e7ab515c672 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb @@ -9,7 +9,7 @@ module QA # pipeline created (Sidekiq read/write) -> # runner picks up pipeline (API read/write) -> # User views pipeline succeeds (Web read) - RSpec.describe 'Verify', :runner, product_group: :pipeline_insights do + RSpec.describe 'Verify', :runner, product_group: :pipeline_security do context 'Endpoint Coverage' do let!(:project) do Resource::Project.fabricate_via_api! do |project| diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb index b45ccfa5433..ee60483d4de 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Verify', :runner, product_group: :pipeline_insights do + RSpec.describe 'Verify', :runner, product_group: :pipeline_execution do describe 'Code coverage statistics' do let(:executor) { "qa-runner-#{Time.now.to_i}" } let(:runner) do diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb index f95bcc59db1..6252a287fd4 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb @@ -36,6 +36,8 @@ module QA use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project) use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project) when :ci_job_token + package_project_inbound_job_token_disabled + client_project_inbound_job_token_disabled '${CI_JOB_TOKEN}' when :project_deploy_token use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: package_project) @@ -43,10 +45,7 @@ module QA end end - it "pushes and pulls a helm chart", testcase: params[:testcase], quarantine: { - type: :stale, - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391649' - } do + it "pushes and pulls a helm chart", testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| helm_upload_yaml = ERB.new(read_fixture('package_managers/helm', 'helm_upload_package.yaml.erb')).result(binding) diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb index 3fb5c921187..879bb7022c8 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module QA - RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry do + RSpec.describe 'Package', :orchestrated, :packages, :object_storage, :reliable, product_group: :package_registry, feature_flag: { name: 'maven_central_request_forwarding', scope: :global } do describe 'Maven group level endpoint' do include Runtime::Fixtures include Support::Helpers::MaskToken @@ -41,11 +41,7 @@ module QA 'using a ci job token' => { authentication_token_type: :ci_job_token, maven_header_name: 'Job-Token', - testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579', - quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373189', - type: :stale - } + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579' } } end @@ -57,6 +53,8 @@ module QA use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: package_project) use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: client_project) when :ci_job_token + package_project_inbound_job_token_disabled + client_project_inbound_job_token_disabled '${CI_JOB_TOKEN}' when :project_deploy_token use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: package_project) @@ -64,7 +62,7 @@ module QA end end - it 'pushes and pulls a maven package', testcase: params[:testcase], quarantine: params[:quarantine] do + it 'pushes and pulls a maven package', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding) @@ -223,5 +221,98 @@ module QA end end end + + describe 'Maven request forwarding' do + include Runtime::Fixtures + + let(:group_id) { 'com.gitlab.qa' } + let(:artifact_id) { "maven-#{SecureRandom.hex(8)}" } + let(:package_name) { "#{group_id}/#{artifact_id}".tr('.', '/') } + let(:package_version) { '1.3.7' } + let(:personal_access_token) { Runtime::Env.personal_access_token } + let(:group) { Resource::Group.fabricate_via_api! } + + let(:imported_project) do + Resource::ProjectImportedFromURL.fabricate_via_browser_ui! do |project| + project.name = "maven_imported_project" + project.group = group + project.gitlab_repository_path = 'https://gitlab.com/gitlab-org/quality/imported-projects/maven.git' + end + end + + let(:gitlab_address_with_port) do + uri = URI.parse(Runtime::Scenario.gitlab_address) + "#{uri.scheme}://#{uri.host}:#{uri.port}" + end + + let(:package) do + Resource::Package.init do |package| + package.name = package_name + package.project = imported_project + end + end + + let(:runner) do + Resource::ProjectRunner.fabricate! do |runner| + runner.name = "qa-runner-#{Time.now.to_i}" + runner.tags = ["runner-for-#{imported_project.group.name}"] + runner.executor = :docker + runner.token = imported_project.group.reload!.runners_token + end + end + + before do + Runtime::Feature.enable(:maven_central_request_forwarding) + Flow::Login.sign_in_unless_signed_in + + imported_project + runner + end + + after do + Runtime::Feature.disable(:maven_central_request_forwarding) + + runner.remove_via_api! + package.remove_via_api! + imported_project.remove_via_api! + end + + it( + 'uses GitLab as a mirror of the central proxy', + :skip_live_env, + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/375988', + quarantine: { + type: :investigating, + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/378221' + } + ) do + Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do + Resource::Repository::Commit.fabricate_via_api! do |commit| + settings_xml = ERB.new(read_fixture('package_managers/maven/group/consumer/request_forwarding', 'settings.xml.erb')).result(binding) + gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/consumer/request_forwarding', 'gitlab_ci.yaml.erb')).result(binding) + + commit.project = imported_project + commit.commit_message = 'Add files' + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gitlab_ci_yaml }, + { file_path: 'settings.xml', content: settings_xml } + ]) + end + end + + imported_project.visit! + + Flow::Pipeline.visit_latest_pipeline + + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('install') + end + + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end + end + end end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb index d86ce09c4e1..9a418f11b1b 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb @@ -25,16 +25,15 @@ module QA when :personal_access_token "\"#{personal_access_token}\"" when :ci_job_token + package_project_inbound_job_token_disabled + client_project_inbound_job_token_disabled 'System.getenv("CI_JOB_TOKEN")' when :project_deploy_token "\"#{project_deploy_token.token}\"" end end - it 'pushes and pulls a maven package via gradle', testcase: params[:testcase], quarantine: { - type: :stale, - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391650' - } do + it 'pushes and pulls a maven package via gradle', testcase: params[:testcase] do Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do Resource::Repository::Commit.fabricate_via_api! do |commit| gradle_upload_yaml = ERB.new(read_fixture('package_managers/maven/gradle', 'gradle_upload_package.yaml.erb')).result(binding) diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb index c2cbec3fbb7..48b9fdec2e9 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb @@ -50,6 +50,20 @@ module QA end end + let(:package_project_inbound_job_token_disabled) do + Resource::CICDSettings.fabricate_via_api! do |settings| + settings.project_path = project.full_path + settings.inbound_job_token_scope_enabled = false + end + end + + let(:client_project_inbound_job_token_disabled) do + Resource::CICDSettings.fabricate_via_api! do |settings| + settings.project_path = another_project.full_path + settings.inbound_job_token_scope_enabled = false + end + end + let!(:runner) do Resource::GroupRunner.fabricate! do |runner| runner.name = "qa-runner-#{Time.now.to_i}" @@ -79,6 +93,8 @@ module QA use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token.token, project: project) use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token.token, project: another_project) when :ci_job_token + package_project_inbound_job_token_disabled + client_project_inbound_job_token_disabled '${CI_JOB_TOKEN}' when :group_deploy_token use_ci_variable(name: 'GROUP_DEPLOY_TOKEN', value: group_deploy_token.token, project: project) @@ -97,10 +113,7 @@ module QA end end - it 'publishes a nuget package at the project endpoint and installs it from the group endpoint', testcase: params[:testcase], quarantine: { - type: :stale, - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391648' - } do + it 'publishes a nuget package at the project endpoint and installs it from the group endpoint', testcase: params[:testcase] do Flow::Login.sign_in Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb index 0f6bee951a7..51006dd1e38 100644 --- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb +++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb @@ -19,7 +19,6 @@ module QA resource.project = project resource.name = runner_name resource.tags = [runner_name] - resource.image = 'gitlab/gitlab-runner:alpine-v15.8.3' end end diff --git a/qa/qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb new file mode 100644 index 00000000000..433d286686b --- /dev/null +++ b/qa/qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Monitor', product_group: :respond do + describe 'Recovery alert' do + shared_examples 'triggers recovery alert' do + it 'only resolves the correct alert', :aggregate_failures do + Page::Project::Menu.perform(&:go_to_monitor_alerts) + Page::Project::Monitor::Alerts::Index.perform do |index| + # Open tab is displayed by default + expect(index).to have_alert_with_title(unresolve_title) + expect(index).to have_no_alert_with_title(resolve_title) + + index.go_to_tab('Resolved') + expect(index).to have_alert_with_title(resolve_title) + expect(index).to have_no_alert_with_title(unresolve_title) + end + end + end + + before do + Flow::Login.sign_in + project.visit! + Flow::AlertSettings.go_to_monitor_settings + end + + context( + 'when using HTTP endpoint integration', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/393589' + ) do + include_context 'sends and resolves test alerts' + + it_behaves_like 'triggers recovery alert' + end + + context( + 'when using Prometheus integration', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/393590' + ) do + include_context 'sends and resolves test alerts' + + let(:http) { false } + + it_behaves_like 'triggers recovery alert' + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/8_monitor/incident_management/recovery_alert_closes_correct_incident.rb b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/recovery_alert_closes_correct_incident.rb new file mode 100644 index 00000000000..fe3cd5a432b --- /dev/null +++ b/qa/qa/specs/features/browser_ui/8_monitor/incident_management/recovery_alert_closes_correct_incident.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Monitor', product_group: :respond do + describe 'Recovery alert' do + shared_examples 'triggers recovery alert' do + it 'only closes the correct incident', :aggregate_failures do + Page::Project::Menu.perform(&:go_to_monitor_incidents) + Page::Project::Monitor::Incidents::Index.perform do |index| + # Open tab is displayed by default + expect(index).to have_incident(title: unresolve_title) + expect(index).to have_no_incident(title: resolve_title) + + index.go_to_tab('Closed') + expect(index).to have_incident(title: resolve_title) + expect(index).to have_no_incident(title: unresolve_title) + end + end + end + + before do + Flow::Login.sign_in + project.visit! + Flow::AlertSettings.go_to_monitor_settings + Flow::AlertSettings.enable_create_incident + end + + context( + 'when using HTTP endpoint integration', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/393842' + ) do + include_context 'sends and resolves test alerts' + + it_behaves_like 'triggers recovery alert' + end + + context( + 'when using Prometheus integration', + testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/393843' + ) do + include_context 'sends and resolves test alerts' + + let(:http) { false } + + it_behaves_like 'triggers recovery alert' + end + end + end +end diff --git a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb index 481a09f601b..d72144cecec 100644 --- a/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb +++ b/qa/qa/specs/features/shared_contexts/import/github_import_shared_context.rb @@ -26,6 +26,7 @@ module QA let(:imported_project) do Resource::ProjectImportedFromGithub.fabricate_via_api! do |project| project.name = 'imported-project' + project.github_repo_id = '466994992' project.group = group project.github_personal_access_token = Runtime::Env.github_access_token project.github_repository_path = github_repo diff --git a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb index 728907c708f..900245deca3 100644 --- a/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb +++ b/qa/qa/specs/features/shared_contexts/import/gitlab_project_migration_common.rb @@ -25,7 +25,6 @@ module QA end before do - Runtime::Feature.enable(:bulk_import_projects) unless Runtime::Feature.enabled?(:bulk_import_projects) source_project # fabricate source group and project end end diff --git a/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb b/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb index a611a801b11..5ab7bb331c0 100644 --- a/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb +++ b/qa/qa/specs/features/shared_contexts/packages_registry_shared_context.rb @@ -20,6 +20,20 @@ module QA end end + let(:package_project_inbound_job_token_disabled) do + Resource::CICDSettings.fabricate_via_api! do |settings| + settings.project_path = package_project.full_path + settings.inbound_job_token_scope_enabled = false + end + end + + let(:client_project_inbound_job_token_disabled) do + Resource::CICDSettings.fabricate_via_api! do |settings| + settings.project_path = client_project.full_path + settings.inbound_job_token_scope_enabled = false + end + end + let(:package) do Resource::Package.init do |package| package.name = package_name diff --git a/qa/qa/specs/features/shared_contexts/sends_and_resolves_test_alerts.rb b/qa/qa/specs/features/shared_contexts/sends_and_resolves_test_alerts.rb new file mode 100644 index 00000000000..a72140f41e0 --- /dev/null +++ b/qa/qa/specs/features/shared_contexts/sends_and_resolves_test_alerts.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module QA + RSpec.shared_context 'sends and resolves test alerts' do + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'project-for-alerts' + project.description = 'Project for alerts' + end + end + + let(:resolve_title) { Faker::Lorem.sentence } + let(:unresolve_title) { Faker::Lorem.sentence } + let(:http) { true } + + let(:payload_to_be_resolved) do + payload(resolve_title, http) + end + + let(:unresolved_payload) do + payload(unresolve_title, http) + end + + before do + http ? Flow::AlertSettings.setup_http_endpoint_integration : Flow::AlertSettings.setup_prometheus_integration + + [payload_to_be_resolved, unresolved_payload].each do |payload| + Flow::AlertSettings.send_test_alert(payload: payload) + end + + mark_as_resolved(payload_to_be_resolved, http) + Flow::AlertSettings.send_test_alert(payload: payload_to_be_resolved) + end + + private + + def mark_as_resolved(payload, http) + if http + payload[:end_time] = Time.now + else + payload[:alerts][0][:status] = 'resolved' + payload[:alerts][0][:endsAt] = Time.now + end + end + + def payload(title, http) + if http + { title: title, description: title } + else + { + version: '4', + groupKey: nil, + status: 'firing', + receiver: '', + groupLabels: {}, + commonLabels: {}, + commonAnnotations: {}, + externalURL: '', + alerts: [ + { + startsAt: Time.now, + generatorURL: Faker::Internet.url, + endsAt: nil, + status: nil, + labels: { gitlab_environment_name: Faker::Lorem.word }, + annotations: { title: title } + } + ] + } + end + end + end +end diff --git a/qa/qa/specs/helpers/context_selector.rb b/qa/qa/specs/helpers/context_selector.rb index dfb00cb807b..59ab7c9722e 100644 --- a/qa/qa/specs/helpers/context_selector.rb +++ b/qa/qa/specs/helpers/context_selector.rb @@ -22,7 +22,7 @@ module QA opts = {} opts[:domain] = '.+' - opts[:tld] = '.com' + opts[:tld] = opts_tld uri = URI(Runtime::Scenario.gitlab_address) @@ -79,6 +79,10 @@ module QA def production_domain GitlabEdition.jh? ? 'jihulab' : 'gitlab' end + + def opts_tld + GitlabEdition.jh? ? '(.com|.hk)' : '.com' + end end end end diff --git a/qa/qa/support/json_formatter.rb b/qa/qa/support/json_formatter.rb index 252ccfe73d3..178b76e1974 100644 --- a/qa/qa/support/json_formatter.rb +++ b/qa/qa/support/json_formatter.rb @@ -26,6 +26,10 @@ module QA class: exception.class.name, message: exception.message, message_lines: strip_ansi_codes(notification.message_lines), + correlation_id: exception.message[match_data_after(Loglinking::CORRELATION_ID_TITLE)], + sentry_url: exception.message[match_data_after(Loglinking::SENTRY_URL_TITLE)], + kibana_discover_url: exception.message[match_data_after(Loglinking::KIBANA_DISCOVER_URL_TITLE)], + kibana_dashboard_url: exception.message[match_data_after(Loglinking::KIBANA_DASHBOARD_URL_TITLE)], backtrace: notification.formatted_backtrace } end @@ -70,6 +74,10 @@ module QA modified = Array(strings).map { |string| string.dup.gsub(/\x1b\[{1,2}[0-9;:?]*m/m, '') } modified.size == 1 ? modified[0] : modified end + + def match_data_after(title) + /(?<=#{title} ).*/ + end end end end diff --git a/qa/qa/support/loglinking.rb b/qa/qa/support/loglinking.rb index e9202af3965..ac6e596ef6f 100644 --- a/qa/qa/support/loglinking.rb +++ b/qa/qa/support/loglinking.rb @@ -4,16 +4,22 @@ module QA module Support module Loglinking # Static address variables declared for mapping environment to logging URLs - STAGING_ADDRESS = 'https://staging.gitlab.com' - STAGING_REF_ADDRESS = 'https://staging-ref.gitlab.com' - PRODUCTION_ADDRESS = 'https://gitlab.com' - PRE_PROD_ADDRESS = 'https://pre.gitlab.com' + STAGING_ADDRESS = 'https://staging.gitlab.com' + STAGING_REF_ADDRESS = 'https://staging-ref.gitlab.com' + PRODUCTION_ADDRESS = 'https://gitlab.com' + PRE_PROD_ADDRESS = 'https://pre.gitlab.com' + + # Text titles used for labeling various IDs and URLs + CORRELATION_ID_TITLE = 'Correlation Id:' + SENTRY_URL_TITLE = 'Sentry Url:' + KIBANA_DISCOVER_URL_TITLE = 'Kibana - Discover Url:' + KIBANA_DASHBOARD_URL_TITLE = 'Kibana - Dashboard Url:' class << self def failure_metadata(correlation_id) return if correlation_id.blank? - errors = ["Correlation Id: #{correlation_id}"] + errors = ["#{CORRELATION_ID_TITLE} #{correlation_id}"] env = logging_environment @@ -24,9 +30,9 @@ module QA kibana_discover_url = kibana.discover_url kibana_dashboard_url = kibana.dashboard_url - errors << "Sentry Url: #{sentry_url}" if sentry_url - errors << "Kibana - Discover Url: #{kibana_discover_url}" if kibana_discover_url - errors << "Kibana - Dashboard Url: #{kibana_dashboard_url}" if kibana_dashboard_url + errors << "#{SENTRY_URL_TITLE} #{sentry_url}" if sentry_url + errors << "#{KIBANA_DISCOVER_URL_TITLE} #{kibana_discover_url}" if kibana_discover_url + errors << "#{KIBANA_DASHBOARD_URL_TITLE} #{kibana_dashboard_url}" if kibana_dashboard_url errors.join("\n") end diff --git a/qa/qa/support/matchers/have_matcher.rb b/qa/qa/support/matchers/have_matcher.rb index 734a8890536..d843949e6b2 100644 --- a/qa/qa/support/matchers/have_matcher.rb +++ b/qa/qa/support/matchers/have_matcher.rb @@ -25,6 +25,9 @@ module QA tag label variable + system_note + alert_with_title + incident ].each do |predicate| RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs| match do |page_object| diff --git a/qa/qa/tools/test_resource_data_processor.rb b/qa/qa/tools/test_resource_data_processor.rb index a86c94b4914..3312285ecc4 100644 --- a/qa/qa/tools/test_resource_data_processor.rb +++ b/qa/qa/tools/test_resource_data_processor.rb @@ -38,7 +38,7 @@ module QA api_path: api_path, fabrication_method: fabrication_method, fabrication_time: fabrication_time, - http_method: resource.api_fabrication_http_method, + http_method: resource.api_fabrication_http_method || :post, timestamp: Time.now.to_s } end diff --git a/qa/qa/vendor/smocker/event_payload.rb b/qa/qa/vendor/smocker/event_payload.rb deleted file mode 100644 index 70998e055ea..00000000000 --- a/qa/qa/vendor/smocker/event_payload.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -module QA - module Vendor - module Smocker - class EventPayload - def initialize(hook_data) - @hook_data = hook_data - end - - def raw - @hook_data - end - - def event - raw[:object_kind]&.to_sym - end - - def event_name - raw[:event_name]&.to_sym - end - - def project_name - raw.dig(:project, :name) - end - - def mr? - event == :merge_request - end - - def issue? - event == :issue - end - - def note? - event == :note - end - - def push? - event == :push - end - - def tag? - event == :tag_push - end - - def wiki? - event == :wiki_page - end - - def subgroup_create? - event_name == :subgroup_create - end - - def subgroup_destroy? - event_name == :subgroup_destroy - end - end - end - end -end diff --git a/qa/qa/vendor/smocker/history_response.rb b/qa/qa/vendor/smocker/history_response.rb index 53d5759ef8b..426bbe024ae 100644 --- a/qa/qa/vendor/smocker/history_response.rb +++ b/qa/qa/vendor/smocker/history_response.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require_relative './event_payload' require 'time' module QA @@ -23,12 +22,6 @@ module QA payload[:request] end - # @return [EventPayload] the request body as a webhook event - def as_hook_event - body = request&.dig(:body) - EventPayload.new body if body - end - # @return [Time] Time request was recieved def received date = request&.dig(:date) diff --git a/qa/qa/vendor/smocker/smocker_api.rb b/qa/qa/vendor/smocker/smocker_api.rb index 359d1497825..111de84ffce 100644 --- a/qa/qa/vendor/smocker/smocker_api.rb +++ b/qa/qa/vendor/smocker/smocker_api.rb @@ -113,6 +113,14 @@ module QA end end + # Returns a stringfied version of the Smocker history + # + # @param session_name [String] the session name for the mock + # @return [String] stringified event payloads + def stringified_history(session_name = nil) + history(session_name).map(&:payload).join("\n") + end + private attr_reader :host, :public_port, :admin_port, :scheme diff --git a/qa/spec/fixtures/ff/bulk_import_projects.yml b/qa/spec/fixtures/ff/async_commit_diff_files.yml index 853389577cf..0cadf592cc1 100644 --- a/qa/spec/fixtures/ff/bulk_import_projects.yml +++ b/qa/spec/fixtures/ff/async_commit_diff_files.yml @@ -1,8 +1,8 @@ --- -name: bulk_import_projects -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68873 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339941 -milestone: '14.3' +name: async_commit_diff_files +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38450 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369439 +milestone: '13.3' type: development -group: group::import +group: group::source code default_enabled: false diff --git a/qa/spec/resource/api_fabricator_spec.rb b/qa/spec/resource/api_fabricator_spec.rb index 337c6772a06..96823ea7ada 100644 --- a/qa/spec/resource/api_fabricator_spec.rb +++ b/qa/spec/resource/api_fabricator_spec.rb @@ -116,7 +116,7 @@ RSpec.describe QA::Resource::ApiFabricator do expect { subject.fabricate_via_api! }.to raise_error do |error| expect(error.class).to eql(described_class::ResourceFabricationFailedError) - expect(error.to_s).to eql(<<~ERROR.chomp) + expect(error.to_s).to eql(<<~ERROR.strip) Fabrication of FooBarResource using the API failed (400) with `#{raw_post}`.\n ERROR end diff --git a/qa/spec/resource/project_web_hook_spec.rb b/qa/spec/resource/project_web_hook_spec.rb new file mode 100644 index 00000000000..bca95124c06 --- /dev/null +++ b/qa/spec/resource/project_web_hook_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module QA + RSpec.describe Resource::ProjectWebHook do + let(:smocker_api) { instance_double(Vendor::Smocker::SmockerApi) } + let(:smocker_docker) { class_double(Service::DockerRun::Smocker) } + let(:history_entries) do + [ + { + request: { + body: { + object_kind: 'tag_push' + } + } + }, + { + request: { + body: { + object_kind: 'merge_request' + } + } + } + ] + end + + let(:history_response) { Struct.new(:body).new(history_entries.to_json) } + + it 'configures the project hook events' do + setup_mocks + + described_class.setup(pipeline: true, wiki_page: true) do |webhook, _| + expect(webhook.pipeline_events).to be(true) + expect(webhook.wiki_page_events).to be(true) + expect(webhook.push_events).to be(false) + end + end + + it 'adds an #event method to the smocker object that returns webhook events' do + setup_mocks + + # rubocop:disable RSpec/AnyInstanceOf + expect_any_instance_of(Vendor::Smocker::SmockerApi).to receive(:get_session_id) + .and_return('123') + expect_any_instance_of(Vendor::Smocker::SmockerApi).to receive(:get) + .with(/history/) + .and_return(history_response) + # rubocop:enable RSpec/AnyInstanceOf + + described_class.setup do |_, smocker| + expect(smocker.events('123')).to include( + a_hash_including(object_kind: 'merge_request'), + a_hash_including(object_kind: 'tag_push') + ) + end + end + + def setup_mocks + # rubocop:disable RSpec/AnyInstanceOf + expect_any_instance_of(Vendor::Smocker::SmockerApi).to receive(:reset) + expect_any_instance_of(Vendor::Smocker::SmockerApi).to receive(:register) + # rubocop:enable RSpec/AnyInstanceOf + + expect(Service::DockerRun::Smocker).to receive(:init) + .and_yield(Vendor::Smocker::SmockerApi.new(host: 'smocker.net')) + allow(subject).to receive(:project) + allow(described_class).to receive(:fabricate_via_api!) + .and_yield(subject) + end + end +end diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index e9c2000681b..66720937007 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -189,14 +189,14 @@ RSpec.describe QA::Runtime::Env do end describe '.github_access_token' do - it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do - stub_env('GITHUB_ACCESS_TOKEN', nil) + it 'returns "" if QA_GITHUB_ACCESS_TOKEN is not defined' do + stub_env('QA_GITHUB_ACCESS_TOKEN', nil) expect(described_class.github_access_token).to eq('') end - it 'returns stripped string if GITHUB_ACCESS_TOKEN is defined' do - stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ') + it 'returns stripped string if QA_GITHUB_ACCESS_TOKEN is defined' do + stub_env('QA_GITHUB_ACCESS_TOKEN', ' abc123 ') expect(described_class.github_access_token).to eq('abc123') end end @@ -229,14 +229,14 @@ RSpec.describe QA::Runtime::Env do end describe '.require_github_access_token!' do - it 'raises ArgumentError if GITHUB_ACCESS_TOKEN is not defined' do - stub_env('GITHUB_ACCESS_TOKEN', nil) + it 'raises ArgumentError if QA_GITHUB_ACCESS_TOKEN is not defined' do + stub_env('QA_GITHUB_ACCESS_TOKEN', nil) expect { described_class.require_github_access_token! }.to raise_error(ArgumentError) end - it 'does not raise if GITHUB_ACCESS_TOKEN is defined' do - stub_env('GITHUB_ACCESS_TOKEN', ' abc123 ') + it 'does not raise if QA_GITHUB_ACCESS_TOKEN is defined' do + stub_env('QA_GITHUB_ACCESS_TOKEN', ' abc123 ') expect { described_class.require_github_access_token! }.not_to raise_error end diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb index 7541bb45d82..9e46933542e 100644 --- a/qa/spec/specs/helpers/context_selector_spec.rb +++ b/qa/spec/specs/helpers/context_selector_spec.rb @@ -43,6 +43,7 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do end it 'matches multiple subdomains' do + allow(GitlabEdition).to receive(:jh?).and_return(false) QA::Runtime::Scenario.define(:gitlab_address, "https://staging.gitlab.com") aggregate_failures do @@ -51,13 +52,35 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do end end + it 'matches multiple subdomains on jh side' do + allow(GitlabEdition).to receive(:jh?).and_return(true) + QA::Runtime::Scenario.define(:gitlab_address, "https://staging.jihulab.com") + + aggregate_failures do + expect(described_class.context_matches?(subdomain: [:release, :staging])).to be_truthy + expect(described_class.context_matches?(:production, subdomain: [:release, :staging])).to be_truthy + end + end + it 'matches :production' do + allow(GitlabEdition).to receive(:jh?).and_return(false) QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.com/") expect(described_class.context_matches?(:production)).to be_truthy end + it 'matches :production on jh side' do + allow(GitlabEdition).to receive(:jh?).and_return(true) + + QA::Runtime::Scenario.define(:gitlab_address, "https://jihulab.com/") + expect(described_class.context_matches?(:production)).to be_truthy + + QA::Runtime::Scenario.define(:gitlab_address, "https://jihulab.hk/") + expect(described_class.context_matches?(:production)).to be_truthy + end + it 'matches domain' do + allow(GitlabEdition).to receive(:jh?).and_return(false) QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com') aggregate_failures do @@ -67,6 +90,26 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do end end + it 'matches domain on jh side' do + # To simulate run tests in JH + allow(GitlabEdition).to receive(:jh?).and_return(true) + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com') + + aggregate_failures do + expect(described_class.context_matches?(:production)).to be_truthy + expect(described_class.context_matches?(domain: 'gitlab')).to be_falsey + expect(described_class.context_matches?(domain: 'jihulab')).to be_truthy + end + + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.hk') + + aggregate_failures do + expect(described_class.context_matches?(:production)).to be_truthy + expect(described_class.context_matches?(domain: 'gitlab')).to be_falsey + expect(described_class.context_matches?(domain: 'jihulab')).to be_truthy + end + end + it 'matches tld' do QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.cn') @@ -119,6 +162,7 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do context 'with different environment set' do before do + allow(GitlabEdition).to receive(:jh?).and_return(false) QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.com') end @@ -140,6 +184,31 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do end end end + + context 'with different environment set on jh side' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(true) + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com') + end + + it 'does not run against production' do + group = describe_successfully 'Runs in staging', :something, only: { subdomain: :staging } do + it('runs in staging') {} + end + + expect(group.examples[0].execution_result.status).to eq(:pending) + end + + context 'when excluding contexts' do + it 'runs against production' do + group = describe_successfully 'Runs in staging', :something, except: { subdomain: :staging } do + it('runs in staging') {} + end + + expect(group.examples[0].execution_result.status).to eq(:passed) + end + end + end end it 'runs only in staging' do @@ -226,6 +295,7 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do context 'production' do before do + allow(GitlabEdition).to receive(:jh?).and_return(false) QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.com/') end @@ -260,6 +330,80 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do end end + context 'jh mainland production ' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(true) + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com/') + end + + it 'runs on production' do + group = describe_successfully do + 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: 'jihulab' }) {} + end + + aggregate_failures do + expect(group.examples[0].execution_result.status).to eq(:passed) + expect(group.examples[1].execution_result.status).to eq(:pending) + expect(group.examples[2].execution_result.status).to eq(:passed) + end + end + + context 'when excluding contexts' do + it 'skips production' do + group = describe_successfully do + it('skips prod', except: :production) {} + it('runs on prod', except: { subdomain: :staging }) {} + it('skips prod and staging', except: { subdomain: /(staging.)?/, domain: 'jihulab' }) {} + end + + aggregate_failures do + 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) + end + end + end + end + + context 'jh hk production ' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(true) + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.hk/') + end + + it 'runs on production' do + group = describe_successfully do + 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: 'jihulab' }) {} + end + + aggregate_failures do + expect(group.examples[0].execution_result.status).to eq(:passed) + expect(group.examples[1].execution_result.status).to eq(:pending) + expect(group.examples[2].execution_result.status).to eq(:passed) + end + end + + context 'when excluding contexts' do + it 'skips production' do + group = describe_successfully do + it('skips prod', except: :production) {} + it('runs on prod', except: { subdomain: :staging }) {} + it('skips prod and staging', except: { subdomain: /(staging.)?/, domain: 'jihulab' }) {} + end + + aggregate_failures do + 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) + end + end + end + end + it 'outputs a message for invalid environments' do group = describe_successfully do it('will skip', only: :production) {} diff --git a/qa/spec/specs/helpers/feature_flag_spec.rb b/qa/spec/specs/helpers/feature_flag_spec.rb index 491fc22f026..180890b2701 100644 --- a/qa/spec/specs/helpers/feature_flag_spec.rb +++ b/qa/spec/specs/helpers/feature_flag_spec.rb @@ -122,6 +122,10 @@ RSpec.describe QA::Specs::Helpers::FeatureFlag do end context 'when run on production' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(false) + end + before(:context) do QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.com') end @@ -147,6 +151,66 @@ RSpec.describe QA::Specs::Helpers::FeatureFlag do it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global } end + context 'when run on jh production mainland' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(true) + end + + before(:context) do + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com') + end + + context 'when no scope is defined' do + it_behaves_like 'skips with given feature flag metadata', { name: 'no_scope_ff' } + + context 'for only one test in the example group' do + it 'only skips specified test and runs all others' do + group = describe_successfully 'Feature flag set for one test' do + it('is skipped', feature_flag: { name: 'single_test_ff' }) {} + it('passes') {} + end + + expect(group.examples[0].execution_result.status).to eq(:pending) + expect(group.examples[1].execution_result.status).to eq(:passed) + end + end + end + + it_behaves_like 'skips with given feature flag metadata', { name: 'actor_ff', scope: :project } + + it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global } + end + + context 'when run on jh production hk' do + before do + allow(GitlabEdition).to receive(:jh?).and_return(true) + end + + before(:context) do + QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.hk') + end + + context 'when no scope is defined' do + it_behaves_like 'skips with given feature flag metadata', { name: 'no_scope_ff' } + + context 'for only one test in the example group' do + it 'only skips specified test and runs all others' do + group = describe_successfully 'Feature flag set for one test' do + it('is skipped', feature_flag: { name: 'single_test_ff' }) {} + it('passes') {} + end + + expect(group.examples[0].execution_result.status).to eq(:pending) + expect(group.examples[1].execution_result.status).to eq(:passed) + end + end + end + + it_behaves_like 'skips with given feature flag metadata', { name: 'actor_ff', scope: :project } + + it_behaves_like 'skips with given feature flag metadata', { name: 'global_ff', scope: :global } + end + context 'when run on pre' do before(:context) do QA::Runtime::Scenario.define(:gitlab_address, 'https://pre.gitlab.com') diff --git a/qa/spec/tools/ci/ff_changes_spec.rb b/qa/spec/tools/ci/ff_changes_spec.rb index 71ca26867e0..d0bf6de148d 100644 --- a/qa/spec/tools/ci/ff_changes_spec.rb +++ b/qa/spec/tools/ci/ff_changes_spec.rb @@ -12,7 +12,7 @@ RSpec.describe QA::Tools::Ci::FfChanges do let(:mr_diff) do [ { - path: "config/feature_flags/development/bulk_import_projects.yml", + path: "config/feature_flags/development/async_commit_diff_files.yml", deleted_file: deleted_file } ] @@ -21,12 +21,12 @@ RSpec.describe QA::Tools::Ci::FfChanges do before do allow(File).to receive(:read) .with(File.expand_path("../#{mr_diff.first[:path]}", QA::Runtime::Path.qa_root)) - .and_return(File.read("spec/fixtures/ff/bulk_import_projects.yml")) + .and_return(File.read("spec/fixtures/ff/async_commit_diff_files.yml")) end context "with changed feature flag" do it "returns inverse ff state option" do - expect(ff_changes.fetch).to eq("bulk_import_projects=enabled") + expect(ff_changes.fetch).to eq("async_commit_diff_files=enabled") end end @@ -34,7 +34,7 @@ RSpec.describe QA::Tools::Ci::FfChanges do let(:deleted_file) { true } it "returns deleted ff state option" do - expect(ff_changes.fetch).to eq("bulk_import_projects=deleted") + expect(ff_changes.fetch).to eq("async_commit_diff_files=deleted") end end end diff --git a/qa/spec/vendor/smocker_api_spec.rb b/qa/spec/vendor/smocker_api_spec.rb new file mode 100644 index 00000000000..b54197b8b1f --- /dev/null +++ b/qa/spec/vendor/smocker_api_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module QA + RSpec.describe Vendor::Smocker::SmockerApi do + let(:host) { 'smocker.bar' } + + subject { described_class.new(host: host) } + + it 'retries until the service is ready' do + expect(subject).to receive(:get) + .and_raise(StandardError) + .and_raise(StandardError) + .and_return(200) + + expect { subject.wait_for_ready }.not_to raise_error + end + end +end |