diff options
Diffstat (limited to 'qa/qa')
34 files changed, 781 insertions, 89 deletions
diff --git a/qa/qa/fixtures/kubernetes_agent/agentk-config.yaml.erb b/qa/qa/fixtures/kubernetes_agent/agentk-config.yaml.erb new file mode 100644 index 00000000000..2d29cdecbcc --- /dev/null +++ b/qa/qa/fixtures/kubernetes_agent/agentk-config.yaml.erb @@ -0,0 +1,3 @@ +gitops: + manifest_projects: + - id: <%= project.full_path %>
\ No newline at end of file diff --git a/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb new file mode 100644 index 00000000000..a13c92d5c6d --- /dev/null +++ b/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb @@ -0,0 +1,95 @@ +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 %>" # Use this for GitLab chart deployments + # - "<%= kas_grpc_address %>" # Use this for GDK + volumeMounts: + - name: token-volume + mountPath: /config + 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/kubernetes_agent/galatic-empire-manifest.yaml b/qa/qa/fixtures/kubernetes_agent/galatic-empire-manifest.yaml new file mode 100644 index 00000000000..78c42f2b94d --- /dev/null +++ b/qa/qa/fixtures/kubernetes_agent/galatic-empire-manifest.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: "galatic-empire" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: "imperial-starfleet" + namespace: "galatic-empire" +data: + deathStars: "1" + starDestroyers: "5" + tieFighters: "25" diff --git a/qa/qa/fixtures/web_ide/README.md b/qa/qa/fixtures/web_ide/README.md new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/qa/qa/fixtures/web_ide/README.md diff --git a/qa/qa/fixtures/web_ide/dk.png b/qa/qa/fixtures/web_ide/dk.png Binary files differnew file mode 100644 index 00000000000..1247f2fecd7 --- /dev/null +++ b/qa/qa/fixtures/web_ide/dk.png diff --git a/qa/qa/fixtures/web_ide/logo_sample.svg b/qa/qa/fixtures/web_ide/logo_sample.svg new file mode 100644 index 00000000000..883e7e6cf92 --- /dev/null +++ b/qa/qa/fixtures/web_ide/logo_sample.svg @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> + <!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch --> + <title>Slice 1</title> + <desc>Created with Sketch.</desc> + <script>alert('FAIL')</script> + <defs></defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)"> + <g id="Page-1" sketch:type="MSShapeGroup"> + <g id="Fill-1-+-Group-24"> + <g id="Group-24"> + <g id="Group"> + <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path> + <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path> + <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path> + </g> + </g> + </g> + </g> + </g> + </g> +</svg> diff --git a/qa/qa/fixtures/web_ide/text_file.txt b/qa/qa/fixtures/web_ide/text_file.txt new file mode 100644 index 00000000000..4c605535558 --- /dev/null +++ b/qa/qa/fixtures/web_ide/text_file.txt @@ -0,0 +1 @@ +Simple text diff --git a/qa/qa/flow/sign_up.rb b/qa/qa/flow/sign_up.rb index c790a82d904..b367b7976e8 100644 --- a/qa/qa/flow/sign_up.rb +++ b/qa/qa/flow/sign_up.rb @@ -6,36 +6,39 @@ module QA module_function def sign_up!(user) + Page::Main::Menu.perform(&:sign_out_if_signed_in) Page::Main::Login.perform(&:switch_to_register_page) + Page::Registration::SignUp.perform do |sign_up| + sign_up.fill_new_user_first_name_field(user.first_name) + sign_up.fill_new_user_last_name_field(user.last_name) + sign_up.fill_new_user_username_field(user.username) + sign_up.fill_new_user_email_field(user.email) + sign_up.fill_new_user_password_field(user.password) + sign_up.click_new_user_register_button + end - success = Support::Retrier.retry_until(raise_on_failure: false) do - Page::Registration::SignUp.perform do |sign_up| - sign_up.fill_new_user_first_name_field(user.first_name) - sign_up.fill_new_user_last_name_field(user.last_name) - sign_up.fill_new_user_username_field(user.username) - sign_up.fill_new_user_email_field(user.email) - sign_up.fill_new_user_password_field(user.password) + Page::Registration::Welcome.perform(&:click_get_started_button_if_available) - # Because invisible_captcha would prevent submitting this form - # within 4 seconds, sleep here. This can be removed once we - # implement invisible_captcha as an application setting instead - # of a feature flag, so we can turn it off while testing. - # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/284113 - sleep 5 + success = if user.expect_fabrication_success + Page::Main::Menu.perform(&:has_personal_area?) + else + Page::Main::Menu.perform(&:not_signed_in?) + end - sign_up.click_new_user_register_button - end + raise "Failed user registration attempt. Registration was expected to #{user.expect_fabrication_success ? 'succeed' : 'fail'} but #{success ? 'succeeded' : 'failed'}." unless success + end - Page::Registration::Welcome.perform(&:click_get_started_button_if_available) + def disable_sign_ups + Flow::Login.sign_in_as_admin + Page::Main::Menu.perform(&:go_to_admin_area) + Page::Admin::Menu.perform(&:go_to_general_settings) - if user.expect_fabrication_success - Page::Main::Menu.perform(&:has_personal_area?) - else - Page::Main::Menu.perform(&:not_signed_in?) + Page::Admin::Settings::General.perform do |general_settings| + general_settings.expand_sign_up_restrictions do |signup_settings| + signup_settings.disable_signups + signup_settings.save_changes end end - - raise "Failed to register the user" unless success end end end diff --git a/qa/qa/page/admin/settings/component/sign_up_restrictions.rb b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb index 8c5b4bf506b..9526faf4126 100644 --- a/qa/qa/page/admin/settings/component/sign_up_restrictions.rb +++ b/qa/qa/page/admin/settings/component/sign_up_restrictions.rb @@ -8,6 +8,7 @@ module QA class SignUpRestrictions < Page::Base view 'app/views/admin/application_settings/_signup.html.haml' do element :require_admin_approval_after_user_signup_checkbox + element :signup_enabled_checkbox element :save_changes_button end @@ -15,6 +16,11 @@ module QA check_element :require_admin_approval_after_user_signup_checkbox click_element :save_changes_button end + + def disable_signups + uncheck_element :signup_enabled_checkbox + click_element :save_changes_button + end end end end diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 00b48dc7fe9..99f73bbba48 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -135,8 +135,11 @@ module QA def check_element(name) retry_until(sleep_interval: 1) do find_element(name).set(true) + checked = find_element(name).checked? - find_element(name).checked? + QA::Runtime::Logger.debug(checked ? "#{name} was checked" : "#{name} was not checked") + + checked end end @@ -185,19 +188,31 @@ module QA end def has_element?(name, **kwargs) - wait_for_requests(skip_finished_loading_check: !!kwargs.delete(:skip_finished_loading_check)) - disabled = kwargs.delete(:disabled) + original_kwargs = kwargs.dup + wait = kwargs.delete(:wait) || Capybara.default_max_wait_time + text = kwargs.delete(:text) + klass = kwargs.delete(:class) - if disabled.nil? - wait = kwargs.delete(:wait) || Capybara.default_max_wait_time - text = kwargs.delete(:text) - klass = kwargs.delete(:class) - - has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass) - else - find_element(name, kwargs).disabled? == disabled + try_find_element = ->(wait) do + if disabled.nil? + has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass) + else + find_element(name, original_kwargs).disabled? == disabled + end end + + # Check for the element before waiting for requests, just in case unrelated requests are in progress. + # This is to avoid waiting unnecessarily after the element we're interested in has already appeared. + return true if try_find_element.call(wait) + + # If the element didn't appear, wait for requests and then check again + wait_for_requests(skip_finished_loading_check: !!kwargs.delete(:skip_finished_loading_check)) + + # We only wait one second now because we previously waited the full expected duration, + # plus however long it took for requests to complete. One second should be enough + # for the UI to update after requests complete. + try_find_element.call(1) end def has_no_element?(name, **kwargs) @@ -274,8 +289,11 @@ module QA sleep 1 end - def within_element(name, text: nil) - page.within(element_selector_css(name), text: text) do + def within_element(name, **kwargs) + wait_for_requests + text = kwargs.delete(:text) + + page.within(element_selector_css(name, kwargs), text: text) do yield end end diff --git a/qa/qa/page/component/snippet.rb b/qa/qa/page/component/snippet.rb index 459c02ec883..b98c429df8c 100644 --- a/qa/qa/page/component/snippet.rb +++ b/qa/qa/page/component/snippet.rb @@ -78,6 +78,11 @@ module QA base.view 'app/assets/javascripts/snippets/components/embed_dropdown.vue' do element :copy_button end + + base.view 'app/assets/javascripts/blob/components/blob_header_default_actions.vue' do + element :default_actions_container + element :copy_contents_button + end end def has_snippet_title?(snippet_title) @@ -182,7 +187,10 @@ module QA def add_comment(comment) fill_element(:note_field, comment) click_element(:comment_button) - wait_until(reload: false) { has_element?(:note_author_content) } + + unless has_element?(:note_author_content) + raise ElementNotFound, "Comment did not appear as expected" + end end def has_comment_author?(author_username) @@ -207,7 +215,10 @@ module QA click_element(:edit_comment_button) fill_element(:edit_note_field, comment) click_element(:save_comment_button) - wait_until(reload: false) { has_element?(:note_author_content) } + + unless has_element?(:note_author_content) + raise ElementNotFound, "Comment did not appear as expected" + end end def delete_comment(comment) @@ -215,7 +226,32 @@ module QA accept_alert do click_element(:delete_comment_button) end - wait_until(reload: false) { has_no_text?(comment) } + + unless has_no_element?(:note_content, text: comment) + raise ElementNotFound, "Comment was not removed as expected" + end + end + + def click_copy_file_contents(file_number = nil) + if file_number + within_element_by_index(:default_actions_container, file_number - 1) do + click_element(:copy_contents_button) + end + else + within_element(:default_actions_container) do + click_element(:copy_contents_button) + end + end + end + + def copy_file_contents_to_comment(file_number = nil) + click_copy_file_contents(file_number) + send_keys_to_element(:note_field, [:shift, :insert]) + click_element(:comment_button) + + unless has_element?(:note_author_content) + raise ElementNotFound, "Comment did not appear as expected" + end end end end diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb index 2ecb27e05b2..8c70d0874c4 100644 --- a/qa/qa/page/project/job/show.rb +++ b/qa/qa/page/project/job/show.rb @@ -12,7 +12,7 @@ module QA end view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do - element :pipeline_path + element :pipeline_path, required: true end view 'app/assets/javascripts/jobs/components/sidebar.vue' do diff --git a/qa/qa/page/project/members.rb b/qa/qa/page/project/members.rb index 88b05ceb1d1..447049ce22a 100644 --- a/qa/qa/page/project/members.rb +++ b/qa/qa/page/project/members.rb @@ -17,6 +17,7 @@ module QA view 'app/views/projects/project_members/index.html.haml' do element :invite_group_tab + element :groups_list_tab end view 'app/views/shared/members/_invite_group.html.haml' do @@ -48,6 +49,7 @@ module QA def remove_group(group_name) click_element :invite_group_tab + click_element :groups_list_tab page.accept_alert do within_element(:group_row, text: group_name) do click_element :delete_group_access_link diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index 7f96b896c1d..994b1c02a3d 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -77,7 +77,7 @@ module QA end def click_job(job_name) - click_element(:job_link, text: job_name) + click_element(:job_link, Project::Job::Show, text: job_name) end def expand_child_pipeline diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb index 757084fc5b9..9c4b3a3c1c3 100644 --- a/qa/qa/page/project/settings/advanced.rb +++ b/qa/qa/page/project/settings/advanced.rb @@ -26,6 +26,7 @@ module QA view 'app/views/projects/_export.html.haml' do element :export_project_link element :download_export_link + element :export_project_content end def update_project_path_to(path) diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb index fc33c753230..a8b30922bd2 100644 --- a/qa/qa/page/project/web_ide/edit.rb +++ b/qa/qa/page/project/web_ide/edit.rb @@ -57,6 +57,7 @@ module QA view 'app/assets/javascripts/vue_shared/components/file_row.vue' do element :file_name_content + element :file_row_container end view 'app/assets/javascripts/ide/components/new_dropdown/index.vue' do @@ -77,9 +78,29 @@ module QA element :ide_commit_message_field end + view 'app/assets/javascripts/vue_shared/components/changed_file_icon.vue' do + element :changed_file_icon_content + end + + view 'app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue' do + element :preview_container + end + + view 'app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue' do + element :download_button + end + + view 'app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue' do + element :image_viewer_container + end + + view 'app/assets/javascripts/ide/components/new_dropdown/upload.vue' do + element :file_upload_field + end + def has_file?(file_name) within_element(:file_list) do - page.has_content? file_name + has_text?(file_name) end end @@ -87,6 +108,26 @@ module QA has_element?(:project_path_content, project_path: project_path) end + def has_file_addition_icon?(file_name) + within_element(:file_row_container, file_name: file_name) do + has_element?(:changed_file_icon_content, title: 'Added') + end + end + + def has_download_button?(file_name) + click_element(:file_row_container, file_name: file_name) + within_element(:preview_container) do + has_element?(:download_button) + end + end + + def has_image_viewer?(file_name) + click_element(:file_row_container, file_name: file_name) + within_element(:preview_container) do + has_element?(:image_viewer_container) + end + end + def go_to_project click_element(:project_path_content, Page::Project::Show) end @@ -212,6 +253,12 @@ module QA has_element?(:file_list) end end + + def upload_file(file_path) + within_element(:file_list) do + find_element(:file_upload_field, visible: false).send_keys(file_path) + end + end end end end diff --git a/qa/qa/resource/clusters/agent.rb b/qa/qa/resource/clusters/agent.rb new file mode 100644 index 00000000000..cad5a4c6b1d --- /dev/null +++ b/qa/qa/resource/clusters/agent.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module QA + module Resource + module Clusters + class Agent < QA::Resource::Base + attribute :id + attribute :name + attribute :project do + QA::Resource::Project.fabricate_via_api! do |project| + project.name = 'project-with-cluster-agent' + end + end + + def initialize + @name = "my-agent" + end + + def fabricate! + puts 'TODO: FABRICATE VIA UI' + end + # TODO + # + # The UI for this model is not yet implemented. So far it can only be + # created through the GraphQL API + # def fabricate + # + # end + + def api_get_path + "gid://gitlab/Clusters::Agent/#{id}" + end + + def api_post_path + "/graphql" + end + + def api_post_body + <<~GQL + mutation createAgent { + createClusterAgent(input: { projectPath: "#{project.full_path}", name: "#{@name}" }) { + clusterAgent { + id + name + } + errors + } + } + GQL + end + end + end + end +end diff --git a/qa/qa/resource/clusters/agent_token.rb b/qa/qa/resource/clusters/agent_token.rb new file mode 100644 index 00000000000..6a5e861b650 --- /dev/null +++ b/qa/qa/resource/clusters/agent_token.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module QA + module Resource + module Clusters + class AgentToken < QA::Resource::Base + attribute :id + attribute :secret + attribute :agent do + QA::Resource::Clusters::Agent.fabricate_via_api! + end + + def fabricate! + puts 'TODO: FABRICATE VIA UI' + end + # TODO + # + # The UI for this model is not yet implemented. So far it can only be + # created through the GraphQL API + # def fabricate + # + # end + + def api_get_path + "gid://gitlab/Clusters::AgentToken/#{id}" + end + + def api_post_path + "/graphql" + end + + def api_post_body + <<~GQL + mutation createToken { + clusterAgentTokenCreate(input: { clusterAgentId: "gid://gitlab/Clusters::Agent/#{agent.id}" }) { + secret # This is the value you need to use on the next step + token { + createdAt + id + } + errors + } + } + GQL + end + end + end + end +end diff --git a/qa/qa/resource/protected_branch.rb b/qa/qa/resource/protected_branch.rb index 51ea9d1c5b9..7eb5442a964 100644 --- a/qa/qa/resource/protected_branch.rb +++ b/qa/qa/resource/protected_branch.rb @@ -22,7 +22,7 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.branch = branch_name - commit.start_branch = 'master' + commit.start_branch = project.default_branch commit.commit_message = 'Add new file' commit.add_files([ { file_path: "new_file-#{SecureRandom.hex(8)}.md", content: 'new file' } diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 24be4e4f978..1c637db5385 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -407,6 +407,10 @@ module QA QA::Runtime::Scenario.attributes.include?(:geo_secondary_address) end + def gitlab_agentk_version + ENV.fetch('GITLAB_AGENTK_VERSION', 'v13.7.0') + end + private def remote_grid_credentials diff --git a/qa/qa/runtime/feature.rb b/qa/qa/runtime/feature.rb index a48bc216ac2..dd7f9cf898c 100644 --- a/qa/qa/runtime/feature.rb +++ b/qa/qa/runtime/feature.rb @@ -32,7 +32,7 @@ module QA def enabled?(key, **scopes) feature = JSON.parse(get_features).find { |flag| flag['name'] == key.to_s } - feature && feature['state'] == 'on' || feature['state'] == 'conditional' && scopes.present? && enabled_scope?(feature['gates'], scopes) + feature && (feature['state'] == 'on' || feature['state'] == 'conditional' && scopes.present? && enabled_scope?(feature['gates'], scopes)) end private diff --git a/qa/qa/scenario/test/integration/ssh_tunnel.rb b/qa/qa/scenario/test/integration/ssh_tunnel.rb new file mode 100644 index 00000000000..926dce1807e --- /dev/null +++ b/qa/qa/scenario/test/integration/ssh_tunnel.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module QA + module Scenario + module Test + module Integration + class SSHTunnel < Test::Instance::All + tags :ssh_tunnel + end + end + end + end +end diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb index 84196556547..ddf97046fb0 100644 --- a/qa/qa/service/kubernetes_cluster.rb +++ b/qa/qa/service/kubernetes_cluster.rb @@ -43,6 +43,14 @@ module QA cluster_name end + def create_secret(secret, secret_name) + shell("kubectl create secret generic #{secret_name} --from-literal=token='#{secret}'") + end + + def apply_manifest(manifest) + shell('kubectl apply -f -', stdin_data: manifest) + end + private def fetch_api_url diff --git a/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb new file mode 100644 index 00000000000..8c3b8d88a29 --- /dev/null +++ b/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + context 'Gitaly', :orchestrated, :mtls do + describe 'Using mTLS' do + let(:intial_commit_message) { 'Initial commit' } + let(:first_added_commit_message) { 'commit over git' } + let(:second_added_commit_message) { 'commit over api' } + + it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1118' do + project = Resource::Project.fabricate! do |project| + project.name = "mTLS" + project.initialize_with_readme = true + end + + Resource::Repository::ProjectPush.fabricate! do |push| + push.project = project + push.new_branch = false + push.commit_message = first_added_commit_message + push.file_content = 'First commit' + end + + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = second_added_commit_message + commit.add_files([ + { + file_path: "file-#{SecureRandom.hex(8)}", + content: 'Second commit' + } + ]) + end + + expect(project.commits.map { |commit| commit[:message].chomp }) + .to include(intial_commit_message) + .and include(first_added_commit_message) + .and include(second_added_commit_message) + end + end + end + end +end diff --git a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb index 548933d2cde..4bd99b4820e 100644 --- a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb +++ b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb @@ -28,13 +28,6 @@ module QA end end - after do - # Delete the .netrc file created during this test so that subsequent tests don't try to use the logins - Git::Repository.perform do |repository| - repository.delete_netrc - end - end - it 'download archives of each user project then check they are different', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/427' do archive_checksums = {} diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb index b9e1ee53246..5f38907f89f 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb @@ -43,7 +43,9 @@ module QA end end - it 'user transfers a project between groups', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/406' do + it 'user transfers a project between groups', + testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/406', + quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/283925', type: :investigating, only: :production } do # Retry is needed here as the target group is not avaliable for transfer right away. QA::Support::Retrier.retry_on_exception(reload_page: page) do Page::File::Show.perform(&:go_to_general_settings) diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb index d58857f6da2..d9b246fc458 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb @@ -98,8 +98,10 @@ module QA before do enable_require_admin_approval_after_user_signup_via_ui - @user = Resource::User.fabricate_via_browser_ui! do |user| - user.expect_fabrication_success = false + Support::Retrier.retry_on_exception do + @user = Resource::User.fabricate_via_browser_ui! do |user| + user.expect_fabrication_success = false + end end end @@ -148,26 +150,34 @@ module QA end def set_require_admin_approval_after_user_signup_via_api(enable_or_disable) - return if Runtime::ApplicationSettings.get_application_settings[:require_admin_approval_after_user_signup] == enable_or_disable + return if get_require_admin_approval_after_user_signup_via_api == enable_or_disable Runtime::ApplicationSettings.set_application_settings(require_admin_approval_after_user_signup: enable_or_disable) sleep 10 # It takes a moment for the setting to come into effect end + def get_require_admin_approval_after_user_signup_via_api + Runtime::ApplicationSettings.get_application_settings[:require_admin_approval_after_user_signup] + end + def enable_require_admin_approval_after_user_signup_via_ui - unless Runtime::ApplicationSettings.get_application_settings[:require_admin_approval_after_user_signup] - Flow::Login.while_signed_in_as_admin do - Page::Main::Menu.perform(&:go_to_admin_area) - QA::Page::Admin::Menu.perform(&:go_to_general_settings) - Page::Admin::Settings::General.perform do |setting| - setting.expand_sign_up_restrictions do |settings| - settings.require_admin_approval_after_user_signup + unless get_require_admin_approval_after_user_signup_via_api + QA::Support::Retrier.retry_until do + Flow::Login.while_signed_in_as_admin do + Page::Main::Menu.perform(&:go_to_admin_area) + QA::Page::Admin::Menu.perform(&:go_to_general_settings) + Page::Admin::Settings::General.perform do |setting| + setting.expand_sign_up_restrictions do |settings| + settings.require_admin_approval_after_user_signup + end end end - end - sleep 10 # It takes a moment for the setting to come into effect + sleep 15 # It takes a moment for the setting to come into effect + + get_require_admin_approval_after_user_signup_via_api + end end end end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb index ff13b769e3a..a2e01398c94 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb @@ -4,8 +4,7 @@ module QA RSpec.describe 'Manage', :smoke do describe 'Project creation' do it 'user creates a new project', - testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/429', - quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/283925', type: :investigating } do + testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/429' do Flow::Login.sign_in created_project = Resource::Project.fabricate_via_browser_ui! do |project| diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb new file mode 100644 index 00000000000..1670ba56064 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Multiple file snippet' do + let(:first_file_content) { 'First file content' } + let(:second_file_content) { 'Second file content' } + let(:third_file_content) { 'Third file content' } + + let(:personal_snippet) do + Resource::Snippet.fabricate_via_api! do |snippet| + snippet.title = 'Personal snippet to copy file contents from' + snippet.file_name = 'First file name' + snippet.file_content = first_file_content + + snippet.add_files do |files| + files.append(name: 'Second file name', content: second_file_content) + files.append(name: 'Third file name', content: third_file_content) + end + end + end + + let(:project_snippet) do + Resource::ProjectSnippet.fabricate_via_api! do |snippet| + snippet.title = 'Project snippet to copy file contents from' + snippet.file_name = 'First file name' + snippet.file_content = first_file_content + + snippet.add_files do |files| + files.append(name: 'Second file name', content: second_file_content) + files.append(name: 'Third file name', content: third_file_content) + end + end + end + + let(:files) do + [ + { + number: 1, + content: first_file_content + }, + { + number: 2, + content: second_file_content + }, + { + number: 3, + content: third_file_content + } + ] + end + + before do + Flow::Login.sign_in + end + + shared_examples 'copying snippet file contents' do |snippet_type| + it "copies file contents of a multi-file #{snippet_type} to a comment and verifies them" do + send(snippet_type).visit! + + files.each do |files| + Page::Dashboard::Snippet::Show.perform do |snippet| + snippet.copy_file_contents_to_comment(files[:number]) + expect(snippet).to have_comment_content(files[:content]) + snippet.delete_comment(files[:content]) + end + end + end + end + + it_behaves_like 'copying snippet file contents', :personal_snippet + it_behaves_like 'copying snippet file contents', :project_snippet + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb index 971c5371d44..751424222ba 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb @@ -7,7 +7,7 @@ module QA Resource::Snippet.fabricate! do |snippet| snippet.title = 'Shared snippet' snippet.visibility = 'Public' - snippet.file_content = 'code.py' + snippet.file_name = 'code.py' snippet.file_content = 'code to be shared' end end 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 new file mode 100644 index 00000000000..d62f894279f --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Upload a file in Web IDE' do + let(:file_path) { File.absolute_path(File.join('qa', 'fixtures', 'web_ide', file_name)) } + + let(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'upload-file-project' + project.initialize_with_readme = true + end + end + + before do + Flow::Login.sign_in + + project.visit! + Page::Project::Show.perform(&:open_web_ide!) + end + + context 'when a file with the same name already exists' do + let(:file_name) { 'README.md' } + + it 'throws an error' do + Page::Project::WebIDE::Edit.perform do |ide| + ide.upload_file(file_path) + end + + expect(page).to have_content('The name "README.md" is already taken in this directory.') + end + end + + context 'when the file is a text file' do + let(:file_name) { 'text_file.txt' } + + it 'shows the Edit tab with the text' do + Page::Project::WebIDE::Edit.perform do |ide| + ide.upload_file(file_path) + + expect(ide).to have_file(file_name) + expect(ide).to have_file_addition_icon(file_name) + expect(ide).to have_text('Simple text') + + ide.commit_changes + + expect(ide).to have_file(file_name) + end + end + end + + context 'when the file is binary' do + let(:file_name) { 'logo_sample.svg' } + + it 'shows a Download button' do + Page::Project::WebIDE::Edit.perform do |ide| + ide.upload_file(file_path) + + expect(ide).to have_file(file_name) + expect(ide).to have_file_addition_icon(file_name) + expect(ide).to have_download_button(file_name) + + ide.commit_changes + + expect(ide).to have_file(file_name) + end + end + end + + context 'when the file is an image' do + let(:file_name) { 'dk.png' } + + it 'shows an image viewer' do + Page::Project::WebIDE::Edit.perform do |ide| + ide.upload_file(file_path) + + expect(ide).to have_file(file_name) + expect(ide).to have_file_addition_icon(file_name) + expect(ide).to have_image_viewer(file_name) + + ide.commit_changes + + expect(ide).to have_file(file_name) + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb index 5410b5023d9..605248e33f7 100644 --- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb @@ -22,10 +22,22 @@ module QA end end - it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943' do + let!(:runner) do + Resource::Runner.fabricate! do |runner| + runner.name = "qa-runner-#{Time.now.to_i}" + runner.tags = ["runner-for-#{project.name}"] + runner.executor = :docker + runner.project = project + end + end + + let!(:gitlab_address_with_port) do uri = URI.parse(Runtime::Scenario.gitlab_address) - gitlab_address_with_port = "#{uri.scheme}://#{uri.host}:#{uri.port}" - pom_xml = { + "#{uri.scheme}://#{uri.host}:#{uri.port}" + end + + let(:pom_xml) do + { file_path: 'pom.xml', content: <<~XML <project> @@ -52,28 +64,33 @@ module QA </project> XML } - settings_xml = { + end + + let(:settings_xml) do + { file_path: 'settings.xml', content: <<~XML - <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> - <servers> - <server> - <id>#{project.name}</id> - <configuration> - <httpHeaders> - <property> - <name>Private-Token</name> - <value>#{auth_token}</value> - </property> - </httpHeaders> - </configuration> - </server> - </servers> - </settings> + <settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> + <servers> + <server> + <id>#{project.name}</id> + <configuration> + <httpHeaders> + <property> + <name>Private-Token</name> + <value>#{auth_token}</value> + </property> + </httpHeaders> + </configuration> + </server> + </servers> + </settings> XML } + end + it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943' do # Use a maven docker container to deploy the package with_fixtures([pom_xml, settings_xml]) do |dir| Service::DockerRun::Maven.new(dir).publish! @@ -99,6 +116,43 @@ module QA expect(index).not_to have_package(package_name) end end + + it 'publishes and downloads a maven package via CI', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1115' do + Resource::Repository::Commit.fabricate_via_api! do |commit| + commit.project = project + commit.commit_message = 'Add .gitlab-ci.yml' + commit.add_files([ + { + file_path: '.gitlab-ci.yml', + content: + <<~YAML + deploy: + image: maven:3.6-jdk-11 + script: + - 'mvn deploy -s settings.xml' + - "mvn dependency:get -Dartifact=#{group_id}:#{artifact_id}:1.0" + only: + - "#{project.default_branch}" + tags: + - "runner-for-#{project.name}" + YAML + }, + settings_xml, + pom_xml + ]) + end + + project.visit! + Flow::Pipeline.visit_latest_pipeline + + Page::Project::Pipeline::Show.perform do |pipeline| + pipeline.click_job('deploy') + end + + Page::Project::Job::Show.perform do |job| + expect(job).to be_successful(timeout: 800) + end + end end end end diff --git a/qa/qa/specs/features/browser_ui/6_release/pages/pages_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pages/pages_pipeline_spec.rb new file mode 100644 index 00000000000..17c53b3ddc9 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/6_release/pages/pages_pipeline_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Release', :smoke, :runner do + describe 'Pages' do + let!(:project) do + Resource::Project.fabricate_via_api! do |project| + project.name = 'jekyll-pages-project' + project.template_name = :jekyll + end + end + + let(:pipeline) do + Resource::Pipeline.fabricate_via_api! do |pipeline| + pipeline.project = project + pipeline.variables = + { key: :CI_PAGES_DOMAIN, value: 'nip.io', variable_type: :env_var }, + { key: :CI_PAGES_URL, value: 'http://127.0.0.1.nip.io', variable_type: :env_var } + end + end + + before do + Flow::Login.sign_in + + Resource::Runner.fabricate_via_api! do |runner| + runner.project = project + runner.executor = :docker + end + + pipeline.visit! + end + + it 'runs a Pages-specific pipeline', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/296937' do + Page::Project::Pipeline::Show.perform do |show| + expect(show).to have_job(:pages) + show.click_job(:pages) + end + + Page::Project::Job::Show.perform do |show| + expect(show).to have_passed(timeout: 300) + end + end + end + end +end diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb index fca46cc2826..e183d711b30 100644 --- a/qa/qa/support/page/logging.rb +++ b/qa/qa/support/page/logging.rb @@ -143,12 +143,12 @@ module QA super end - def within_element(name, text: nil) - log("within element :#{name}") + def within_element(name, **kwargs) + log("within element :#{name} with args #{kwargs}") element = super - log("end within element :#{name}") + log("end within element :#{name} with args #{kwargs}") element end |