diff options
Diffstat (limited to 'qa')
57 files changed, 844 insertions, 147 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile index 74be373b8e8..84dbfae1008 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -47,9 +47,15 @@ RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ apt-get update -y && apt-get install google-cloud-sdk kubectl -y -WORKDIR /home/qa -COPY ./Gemfile* ./ -RUN bundle install -COPY ./ ./ +WORKDIR /home/gitlab/qa +COPY ./qa/Gemfile* /home/gitlab/qa/ +COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/ +# Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in CE +COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/ +COPY ./lib/gitlab.rb /home/gitlab/lib/ +COPY ./INSTALLATION_TYPE /home/gitlab/ +COPY ./VERSION /home/gitlab/ +RUN cd /home/gitlab/qa/ && bundle install +COPY ./qa /home/gitlab/qa ENTRYPOINT ["bin/test"] diff --git a/qa/Gemfile b/qa/Gemfile index 6abc0d622ad..f04ecb13879 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -1,6 +1,7 @@ source 'https://rubygems.org' gem 'gitlab-qa' +gem 'activesupport', '5.2.3' # This should stay in sync with the root's Gemfile gem 'pry-byebug', '~> 3.5.1', platform: :mri gem 'capybara', '~> 2.16.1' gem 'capybara-screenshot', '~> 1.0.18' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index bf051a115b5..d582d77c5cd 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -1,9 +1,9 @@ GEM remote: https://rubygems.org/ specs: - activesupport (5.1.4) + activesupport (5.2.3) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.2) @@ -28,7 +28,7 @@ GEM childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) coderay (1.1.2) - concurrent-ruby (1.0.5) + concurrent-ruby (1.1.5) diff-lcs (1.3) domain_name (0.5.20170404) unf (>= 0.0.5, < 1.0.0) @@ -38,7 +38,7 @@ GEM gitlab-qa (4.0.0) http-cookie (1.0.3) domain_name (~> 0.5) - i18n (0.9.1) + i18n (1.6.0) concurrent-ruby (~> 1.0) knapsack (1.17.1) rake @@ -50,7 +50,7 @@ GEM mime-types-data (3.2016.0521) mini_mime (1.0.0) mini_portile2 (2.4.0) - minitest (5.11.1) + minitest (5.11.3) netrc (0.11.0) nokogiri (1.10.4) mini_portile2 (~> 2.4.0) @@ -94,7 +94,7 @@ GEM childprocess (~> 0.5) rubyzip (~> 1.2, >= 1.2.2) thread_safe (0.3.6) - tzinfo (1.2.4) + tzinfo (1.2.5) thread_safe (~> 0.1) unf (0.1.4) unf_ext @@ -106,6 +106,7 @@ PLATFORMS ruby DEPENDENCIES + activesupport (= 5.2.3) airborne (~> 0.2.13) capybara (~> 2.16.1) capybara-screenshot (~> 1.0.18) diff --git a/qa/README.md b/qa/README.md index bab19665dac..dede3cd2473 100644 --- a/qa/README.md +++ b/qa/README.md @@ -30,7 +30,7 @@ and corresponding views / partials / selectors in CE / EE. Whenever `qa:selectors` job fails in your merge request, you are supposed to fix [page objects](../doc/development/testing_guide/end_to_end/page_objects.md). You should also trigger end-to-end tests -using `package-and-qa` manual action, to test if everything works fine. +using `package-and-qa-manual` manual action, to test if everything works fine. ## How can I use it? @@ -39,6 +39,11 @@ have an instance available you can follow the instructions below to use the [GitLab Development Kit (GDK)][GDK]. This is the recommended option if you would like to contribute to the tests. +Note: GitLab QA uses [Selenium WebDriver](https://www.seleniumhq.org/) via +[Cabybara](http://teamcapybara.github.io/capybara/), and by default it targets Chrome as +the browser to use. You will need to have Chrome (or Chromium) and +[chromedriver](https://chromedriver.chromium.org/) installed / in your `$PATH`. + ### Run the end-to-end tests in a local development environment Follow the GDK instructions to [prepare](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/prepare.md) @@ -100,6 +105,17 @@ If you need to authenticate as a different user, you can provide the GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bundle exec bin/qa Test::Instance::All https://gitlab.example.com ``` +Some QA tests require logging in as an admin user. By default, the QA +tests will use the the same `root` user seeded by the GDK. + +If you need to authenticate with different admin credentials, you can +provide the `GITLAB_ADMIN_USERNAME` and `GITLAB_ADMIN_PASSWORD` +environment variables: + +``` +GITLAB_ADMIN_USERNAME=admin GITLAB_ADMIN_PASSWORD=myadminpassword GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bundle exec bin/qa Test::Instance::All https://gitlab.example.com +``` + If your user doesn't have permission to default sandbox group `gitlab-qa-sandbox`, you could also use another sandbox group by giving `GITLAB_SANDBOX_NAME`: @@ -123,10 +139,11 @@ To set multiple cookies, separate them with the `;` character, for example: `QA_ Once you have made changes to the CE/EE repositories, you may want to build a Docker image to test locally instead of waiting for the `gitlab-ce-qa` or -`gitlab-ee-qa` nightly builds. To do that, you can run from this directory: +`gitlab-ee-qa` nightly builds. To do that, you can run **from the top `gitlab` +directory** (one level up from this directory): ```sh -docker build -t gitlab/gitlab-ce-qa:nightly . +docker build -t gitlab/gitlab-ce-qa:nightly --file ./qa/Dockerfile ./ ``` [GDK]: https://gitlab.com/gitlab-org/gitlab-development-kit/ @@ -4,6 +4,9 @@ $: << File.expand_path(File.dirname(__FILE__)) Encoding.default_external = 'UTF-8' +require_relative '../lib/gitlab' +require_relative '../config/initializers/0_inject_enterprise_edition_module' + module QA ## # GitLab QA runtime classes, mostly singletons. @@ -70,6 +73,7 @@ module QA end module Repository + autoload :Commit, 'qa/resource/repository/commit' autoload :Push, 'qa/resource/repository/push' autoload :ProjectPush, 'qa/resource/repository/project_push' autoload :WikiPush, 'qa/resource/repository/wiki_push' @@ -157,6 +161,10 @@ module QA module Group autoload :New, 'qa/page/group/new' autoload :Show, 'qa/page/group/show' + + module Settings + autoload :General, 'qa/page/group/settings/general' + end end module File @@ -204,6 +212,7 @@ module QA autoload :Main, 'qa/page/project/settings/main' autoload :Repository, 'qa/page/project/settings/repository' autoload :CICD, 'qa/page/project/settings/ci_cd' + autoload :AutoDevops, 'qa/page/project/settings/auto_devops' autoload :DeployKeys, 'qa/page/project/settings/deploy_keys' autoload :DeployTokens, 'qa/page/project/settings/deploy_tokens' autoload :ProtectedBranches, 'qa/page/project/settings/protected_branches' @@ -300,8 +309,10 @@ module QA autoload :Repository, 'qa/page/admin/settings/repository' autoload :General, 'qa/page/admin/settings/general' autoload :MetricsAndProfiling, 'qa/page/admin/settings/metrics_and_profiling' + autoload :Network, 'qa/page/admin/settings/network' module Component + autoload :IpLimits, 'qa/page/admin/settings/component/ip_limits' autoload :RepositoryStorage, 'qa/page/admin/settings/component/repository_storage' autoload :AccountAndLimit, 'qa/page/admin/settings/component/account_and_limit' autoload :PerformanceBar, 'qa/page/admin/settings/component/performance_bar' @@ -336,6 +347,10 @@ module QA module Issuable autoload :Common, 'qa/page/component/issuable/common' end + + module WebIDE + autoload :Alert, 'qa/page/component/web_ide/alert' + end end end @@ -356,6 +371,13 @@ module QA autoload :KubernetesCluster, 'qa/service/kubernetes_cluster' autoload :Omnibus, 'qa/service/omnibus' autoload :Runner, 'qa/service/runner' + + module ClusterProvider + autoload :Base, 'qa/service/cluster_provider/base' + autoload :Gcloud, 'qa/service/cluster_provider/gcloud' + autoload :Minikube, 'qa/service/cluster_provider/minikube' + autoload :K3d, 'qa/service/cluster_provider/k3d' + end end ## diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb index 61ec9854726..7c214da8486 100644 --- a/qa/qa/page/admin/menu.rb +++ b/qa/qa/page/admin/menu.rb @@ -49,6 +49,14 @@ module QA end end + def go_to_network_settings + hover_settings do + within_submenu do + click_element :admin_settings_network_item + end + end + end + private def hover_settings @@ -75,3 +83,5 @@ module QA end end end + +QA::Page::Admin::Menu.prepend_if_ee('QA::EE::Page::Admin::Menu') diff --git a/qa/qa/page/admin/settings/component/ip_limits.rb b/qa/qa/page/admin/settings/component/ip_limits.rb new file mode 100644 index 00000000000..9db2ae8ba58 --- /dev/null +++ b/qa/qa/page/admin/settings/component/ip_limits.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module QA + module Page + module Admin + module Settings + module Component + class IpLimits < Page::Base + view 'app/views/admin/application_settings/_ip_limits.html.haml' do + element :throttle_unauthenticated_checkbox + element :throttle_authenticated_api_checkbox + element :throttle_authenticated_web_checkbox + element :save_changes_button + end + + def enable_throttles + check_element :throttle_unauthenticated_checkbox + check_element :throttle_authenticated_api_checkbox + check_element :throttle_authenticated_web_checkbox + end + + def save_settings + click_element :save_changes_button + end + end + end + end + end + end +end diff --git a/qa/qa/page/admin/settings/network.rb b/qa/qa/page/admin/settings/network.rb new file mode 100644 index 00000000000..fdb8fcda281 --- /dev/null +++ b/qa/qa/page/admin/settings/network.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module QA + module Page + module Admin + module Settings + class Network < Page::Base + include QA::Page::Settings::Common + + view 'app/views/admin/application_settings/network.html.haml' do + element :ip_limits_section + end + + def expand_ip_limits(&block) + expand_section(:ip_limits_section) do + Component::IpLimits.perform(&block) + end + end + end + end + end + end +end diff --git a/qa/qa/page/component/web_ide/alert.rb b/qa/qa/page/component/web_ide/alert.rb new file mode 100644 index 00000000000..0f0623d5ebf --- /dev/null +++ b/qa/qa/page/component/web_ide/alert.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module WebIDE + module Alert + def self.prepended(page) + page.module_eval do + view 'app/assets/javascripts/ide/components/error_message.vue' do + element :flash_alert + end + end + end + + def has_no_alert?(message = nil) + return has_no_element?(:flash_alert) if message.nil? + + within_element(:flash_alert) do + has_no_text?(message) + end + end + end + end + end + end +end diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb index 0c23d7cffbb..378ac793f7b 100644 --- a/qa/qa/page/dashboard/projects.rb +++ b/qa/qa/page/dashboard/projects.rb @@ -29,3 +29,5 @@ module QA end end end + +QA::Page::Dashboard::Projects.prepend_if_ee('QA::EE::Page::Dashboard::Projects') diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb index 92f9181f99d..f5f44909f25 100644 --- a/qa/qa/page/file/show.rb +++ b/qa/qa/page/file/show.rb @@ -32,3 +32,5 @@ module QA end end end + +QA::Page::File::Show.prepend_if_ee('QA::EE::Page::File::Show') diff --git a/qa/qa/page/group/settings/general.rb b/qa/qa/page/group/settings/general.rb new file mode 100644 index 00000000000..07b421f154a --- /dev/null +++ b/qa/qa/page/group/settings/general.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module QA + module Page + module Group + module Settings + class General < QA::Page::Base + view 'app/views/groups/edit.html.haml' do + element :permission_lfs_2fa_section + end + view 'app/views/groups/settings/_permissions.html.haml' do + element :save_permissions_changes_button + end + end + end + end + end +end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 6a415b56e50..72f8e1c3ef0 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -187,3 +187,5 @@ module QA end end end + +QA::Page::MergeRequest::Show.prepend_if_ee('QA::EE::Page::MergeRequest::Show') diff --git a/qa/qa/page/profile/menu.rb b/qa/qa/page/profile/menu.rb index 2d503499e13..99a795a23ef 100644 --- a/qa/qa/page/profile/menu.rb +++ b/qa/qa/page/profile/menu.rb @@ -34,3 +34,5 @@ module QA end end end + +QA::Page::Profile::Menu.prepend_if_ee('QA::EE::Page::Profile::Menu') diff --git a/qa/qa/page/project/commit/show.rb b/qa/qa/page/project/commit/show.rb index 9770b8a657c..ba09dd1b92a 100644 --- a/qa/qa/page/project/commit/show.rb +++ b/qa/qa/page/project/commit/show.rb @@ -9,6 +9,7 @@ module QA element :options_button element :email_patches element :plain_diff + element :commit_sha_content end def select_email_patches @@ -20,6 +21,10 @@ module QA click_element :options_button click_element :plain_diff end + + def commit_sha + find_element(:commit_sha_content).text + end end end end diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb index 5973a5a958e..cc0c4e1e835 100644 --- a/qa/qa/page/project/import/github.rb +++ b/qa/qa/page/project/import/github.rb @@ -9,7 +9,7 @@ module QA view 'app/views/import/github/new.html.haml' do element :personal_access_token_field, 'text_field_tag :personal_access_token' # rubocop:disable QA/ElementWithPattern - element :list_repos_button, "submit_tag _('List your GitHub repositories')" # rubocop:disable QA/ElementWithPattern + element :authenticate_button, "submit_tag _('Authenticate')" # rubocop:disable QA/ElementWithPattern end view 'app/assets/javascripts/import_projects/components/provider_repo_table_row.vue' do diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb index c4383951ec4..f74366f6967 100644 --- a/qa/qa/page/project/issue/index.rb +++ b/qa/qa/page/project/issue/index.rb @@ -9,11 +9,21 @@ module QA element :issue_link, 'link_to issue.title' # rubocop:disable QA/ElementWithPattern end + view 'app/views/shared/issuable/_nav.html.haml' do + element :closed_issues_link + end + def click_issue_link(title) click_link(title) end + + def click_closed_issues_link + click_element :closed_issues_link + end end end end end end + +QA::Page::Project::Issue::Index.prepend_if_ee('QA::EE::Page::Project::Issue::Index') diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index 45dad9bc0ae..52929ece9ed 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -37,6 +37,10 @@ module QA element :dropdown_menu_labels end + view 'app/views/shared/issuable/_close_reopen_button.html.haml' do + element :reopen_issue_button + end + # Adds a comment to an issue # attachment option should be an absolute path def comment(text, attachment: nil, filter: :all_activities) @@ -108,3 +112,5 @@ module QA end end end + +QA::Page::Project::Issue::Show.prepend_if_ee('QA::EE::Page::Project::Issue::Show') diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb index 838d59b59cb..a9226927741 100644 --- a/qa/qa/page/project/menu.rb +++ b/qa/qa/page/project/menu.rb @@ -39,3 +39,5 @@ module QA end end end + +QA::Page::Project::Menu.prepend_if_ee('QA::EE::Page::Project::SubMenus::SecurityCompliance') diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb index 64aab9be056..d0e8011d82d 100644 --- a/qa/qa/page/project/new.rb +++ b/qa/qa/page/project/new.rb @@ -73,3 +73,5 @@ module QA end end end + +QA::Page::Project::New.prepend_if_ee('QA::EE::Page::Project::New') diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb index eb30e0ea02a..fa276f15b8a 100644 --- a/qa/qa/page/project/operations/kubernetes/show.rb +++ b/qa/qa/page/project/operations/kubernetes/show.rb @@ -53,3 +53,5 @@ module QA end end end + +QA::Page::Project::Operations::Kubernetes::Show.prepend_if_ee('QA::EE::Page::Project::Operations::Kubernetes::Show') diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index 284d0957eb8..3dca47a57e9 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -60,3 +60,5 @@ module QA::Page end end end + +QA::Page::Project::Pipeline::Show.prepend_if_ee('QA::EE::Page::Project::Pipeline::Show') diff --git a/qa/qa/page/project/settings/auto_devops.rb b/qa/qa/page/project/settings/auto_devops.rb new file mode 100644 index 00000000000..827d5b072c3 --- /dev/null +++ b/qa/qa/page/project/settings/auto_devops.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module QA + module Page + module Project + module Settings + class AutoDevops < Page::Base + view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do + element :enable_autodevops_checkbox + element :save_changes_button + end + + def enable_autodevops + check_element :enable_autodevops_checkbox + click_element :save_changes_button + end + end + end + end + end +end diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb index ae826fb3a32..45040cf4660 100644 --- a/qa/qa/page/project/settings/ci_cd.rb +++ b/qa/qa/page/project/settings/ci_cd.rb @@ -13,11 +13,6 @@ module QA element :variables_settings_content end - view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do - element :enable_autodevops_checkbox - element :save_changes_button - end - def expand_runners_settings(&block) expand_section(:runners_settings_content) do Settings::Runners.perform(&block) @@ -30,10 +25,9 @@ module QA end end - def enable_auto_devops + def expand_auto_devops(&block) expand_section(:autodevops_settings_content) do - check_element :enable_autodevops_checkbox - click_element :save_changes_button + Settings::AutoDevops.perform(&block) end end end diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb index dbbe62e3b1d..a196fc0123a 100644 --- a/qa/qa/page/project/settings/main.rb +++ b/qa/qa/page/project/settings/main.rb @@ -41,3 +41,5 @@ module QA end end end + +QA::Page::Project::Settings::Main.prepend_if_ee('QA::EE::Page::Project::Settings::Main') diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb index 831166f6373..e3afaceda80 100644 --- a/qa/qa/page/project/settings/mirroring_repositories.rb +++ b/qa/qa/page/project/settings/mirroring_repositories.rb @@ -89,3 +89,5 @@ module QA end end end + +QA::Page::Project::Settings::MirroringRepositories.prepend_if_ee('QA::EE::Page::Project::Settings::MirroringRepositories') diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb index 903b0979614..1e707f1d315 100644 --- a/qa/qa/page/project/settings/protected_branches.rb +++ b/qa/qa/page/project/settings/protected_branches.rb @@ -73,3 +73,5 @@ module QA end end end + +QA::Page::Project::Settings::ProtectedBranches.prepend_if_ee('QA::EE::Page::Project::Settings::ProtectedBranches') diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 9fd668f812b..850a96d87b0 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -131,3 +131,5 @@ module QA end end end + +QA::Page::Project.prepend_if_ee('QA::EE::Page::Project::Show') diff --git a/qa/qa/page/project/sub_menus/issues.rb b/qa/qa/page/project/sub_menus/issues.rb index 8fb8fa06346..d27a250a300 100644 --- a/qa/qa/page/project/sub_menus/issues.rb +++ b/qa/qa/page/project/sub_menus/issues.rb @@ -10,6 +10,7 @@ module QA def self.included(base) base.class_eval do view 'app/views/layouts/nav/sidebar/_project.html.haml' do + element :issue_boards_link element :issues_item element :labels_link element :milestones_link @@ -29,6 +30,14 @@ module QA end end + def go_to_boards + hover_issues do + within_submenu do + click_element(:issue_boards_link) + end + end + end + def go_to_labels hover_issues do within_submenu do diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb index b5a36862389..4d26cadcdfe 100644 --- a/qa/qa/page/project/web_ide/edit.rb +++ b/qa/qa/page/project/web_ide/edit.rb @@ -5,6 +5,7 @@ module QA module Project module WebIDE class Edit < Page::Base + prepend Page::Component::WebIDE::Alert include Page::Component::DropdownFilter view 'app/assets/javascripts/ide/components/activity_bar.vue' do @@ -34,11 +35,19 @@ module QA element :dropdown_filter_input end + view 'app/assets/javascripts/ide/components/commit_sidebar/actions.vue' do + element :commit_to_current_branch_radio + end + view 'app/assets/javascripts/ide/components/commit_sidebar/form.vue' do element :begin_commit_button element :commit_button end + view 'app/assets/javascripts/ide/components/commit_sidebar/new_merge_request_option.vue' do + element :start_new_mr_checkbox + end + def has_file?(file_name) within_element(:file_list) do page.has_content? file_name @@ -100,6 +109,7 @@ module QA # animation is still in process even when the buttons have the # expected visibility. commit_success_msg_shown = retry_until do + click_element :commit_to_current_branch_radio click_element :commit_button wait(reload: false) do @@ -114,3 +124,5 @@ module QA end end end + +QA::Page::Project::WebIDE::Edit.prepend_if_ee('QA::EE::Page::Component::WebIDE::WebTerminalPanel') diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb index 51b2af8b4ef..16ab59352f3 100644 --- a/qa/qa/resource/issue.rb +++ b/qa/qa/resource/issue.rb @@ -3,7 +3,7 @@ module QA module Resource class Issue < Base - attr_writer :description + attr_writer :description, :milestone attribute :project do Project.fabricate! do |resource| @@ -44,7 +44,9 @@ module QA { labels: labels, title: title - } + }.tap do |hash| + hash[:milestone_id] = @milestone.id if @milestone + end end end end diff --git a/qa/qa/resource/label.rb b/qa/qa/resource/label.rb index 3750725c440..b5e88d8aefc 100644 --- a/qa/qa/resource/label.rb +++ b/qa/qa/resource/label.rb @@ -7,6 +7,7 @@ module QA class Label < Base attr_accessor :description, :color + attribute :id attribute :title attribute :project do diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index 93a82094776..4a29a14c5c2 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -15,6 +15,7 @@ module QA attribute :add_name_uuid attribute :description attribute :standalone + attribute :runners_token attribute :group do Group.fabricate! diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb new file mode 100644 index 00000000000..61c2ad6bfc0 --- /dev/null +++ b/qa/qa/resource/repository/commit.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module QA + module Resource + module Repository + class Commit < Base + attr_accessor :author_email, + :author_name, + :branch, + :commit_message, + :file_path, + :sha + + attribute :project do + Project.fabricate! do |resource| + resource.name = 'project-with-commit' + end + end + + def initialize + @commit_message = 'QA Test - Commit message' + end + + def files=(files) + if !files.is_a?(Array) || + files.empty? || + files.any? { |file| !file.has_key?(:file_path) || !file.has_key?(:content) } + raise ArgumentError, "Please provide an array of hashes e.g.: [{file_path: 'file1', content: 'foo'}]" + end + + @files = files + end + + def resource_web_url(resource) + super + rescue ResourceURLMissingError + # this particular resource does not expose a web_url property + end + + def api_get_path + "#{api_post_path}/#{@sha}" + end + + def api_post_path + "/projects/#{CGI.escape(project.path_with_namespace)}/repository/commits" + end + + def api_post_body + { + branch: @branch || "master", + author_email: @author_email || Runtime::User.default_email, + author_name: @author_name || Runtime::User.username, + commit_message: commit_message, + actions: actions + } + end + + def actions + @files.map do |file| + file.merge({ action: "create" }) + end + end + end + end + end +end diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb index 3344ad3360a..9c2e138bade 100644 --- a/qa/qa/resource/runner.rb +++ b/qa/qa/resource/runner.rb @@ -6,9 +6,10 @@ module QA module Resource class Runner < Base attr_writer :name, :tags, :image + attr_accessor :config attribute :project do - Project.fabricate! do |resource| + Project.fabricate_via_api! do |resource| resource.name = 'project-with-ci-cd' resource.description = 'Project with CI/CD Pipelines' end @@ -26,24 +27,27 @@ module QA @image || 'gitlab/gitlab-runner:alpine' end - def fabricate! - project.visit! - - Page::Project::Menu.perform(&:go_to_ci_cd_settings) - + def fabricate_via_api! Service::Runner.new(name).tap do |runner| - Page::Project::Settings::CICD.perform do |settings| - settings.expand_runners_settings do |runners| - runner.pull - runner.token = runners.registration_token - runner.address = runners.coordinator_address - runner.tags = tags - runner.image = image - runner.register! - end - end + runner.pull + runner.token = project.runners_token + runner.address = Runtime::Scenario.gitlab_address + runner.tags = tags + runner.image = image + runner.config = config if config + runner.run_untagged = true + runner.register! end end + + def api_get_path + end + + def api_post_path + end + + def api_post_body + end end end end diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb index e2b1c4c0831..6eff0e87ddc 100644 --- a/qa/qa/resource/sandbox.rb +++ b/qa/qa/resource/sandbox.rb @@ -7,7 +7,7 @@ module QA # creating it if it doesn't yet exist. # class Sandbox < Base - attr_reader :path + attr_accessor :path attribute :id diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb index 663be27a849..25d8f3c0fbb 100644 --- a/qa/qa/runtime/api/client.rb +++ b/qa/qa/runtime/api/client.rb @@ -8,11 +8,12 @@ module QA class Client attr_reader :address, :user - def initialize(address = :gitlab, personal_access_token: nil, is_new_session: true, user: nil) + def initialize(address = :gitlab, personal_access_token: nil, is_new_session: true, user: nil, ip_limits: false) @address = address @personal_access_token = personal_access_token @is_new_session = is_new_session @user = user + enable_ip_limits if ip_limits end def personal_access_token @@ -26,6 +27,24 @@ module QA private + def enable_ip_limits + Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) } + + Runtime::Browser.visit(@address, Page::Main::Login) + Page::Main::Login.perform(&:sign_in_using_admin_credentials) + Page::Main::Menu.perform(&:click_admin_area) + Page::Admin::Menu.perform(&:go_to_network_settings) + + Page::Admin::Settings::Network.perform do |setting| + setting.expand_ip_limits do |page| + page.enable_throttles + page.save_settings + end + end + + Page::Main::Menu.perform(&:sign_out) + end + def create_personal_access_token Page::Main::Menu.perform(&:sign_out) if @is_new_session && Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) } diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb index 310c1dfeeb4..724b499d32f 100644 --- a/qa/qa/runtime/api/request.rb +++ b/qa/qa/runtime/api/request.rb @@ -12,6 +12,10 @@ module QA @session_address = Runtime::Address.new(api_client.address, request_path) end + def mask_url + @session_address.address.sub(/private_token=.*/, "private_token=[****]") + end + def url @session_address.address end diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 2987bb1a213..197008ed8bf 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -68,7 +68,7 @@ module QA options = Selenium::WebDriver.const_get(QA::Runtime::Env.browser.capitalize)::Options.new if QA::Runtime::Env.browser == :chrome - options.add_argument("window-size=1240,1680") + options.add_argument("window-size=1480,2200") # Chrome won't work properly in a Docker container in sandbox mode options.add_argument("no-sandbox") diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index b184eeb1701..594e5712ab2 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -233,3 +233,5 @@ module QA end end end + +QA::Runtime::Env.extend_if_ee('QA::EE::Runtime::Env') diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb index 72004d5b00a..02cecffd4df 100644 --- a/qa/qa/runtime/fixtures.rb +++ b/qa/qa/runtime/fixtures.rb @@ -3,10 +3,19 @@ module QA module Runtime module Fixtures + include Support::Api + + TemplateNotFoundError = Class.new(RuntimeError) + def fetch_template_from_api(api_path, key) request = Runtime::API::Request.new(api_client, "/templates/#{api_path}/#{key}") - get request.url - json_body[:content] + response = get(request.url) + + unless response.code == HTTP_STATUS_OK + raise TemplateNotFoundError, "Template at #{request.mask_url} could not be found (#{response.code}): `#{response}`." + end + + parse_body(response)[:content] end private diff --git a/qa/qa/scenario/test/sanity/selectors.rb b/qa/qa/scenario/test/sanity/selectors.rb index 632a0f5f2a9..99497cbe0ad 100644 --- a/qa/qa/scenario/test/sanity/selectors.rb +++ b/qa/qa/scenario/test/sanity/selectors.rb @@ -56,3 +56,5 @@ module QA end end end + +QA::Scenario::Test::Sanity::Selectors.prepend_if_ee('QA::EE::Scenario::Test::Sanity::Selectors') diff --git a/qa/qa/service/cluster_provider/base.rb b/qa/qa/service/cluster_provider/base.rb new file mode 100644 index 00000000000..a9678557aca --- /dev/null +++ b/qa/qa/service/cluster_provider/base.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module QA + module Service + module ClusterProvider + class Base + include Service::Shellout + + attr_reader :rbac + + def initialize(rbac:) + @rbac = rbac + end + + def cluster_name + @cluster_name ||= "qa-cluster-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}-#{SecureRandom.hex(4)}" + end + + def set_credentials(admin_user) + raise NotImplementedError + end + + def validate_dependencies + raise NotImplementedError + end + + def setup + raise NotImplementedError + end + + def teardown + raise NotImplementedError + end + + def filter_credentials(credentials) + credentials + end + end + end + end +end diff --git a/qa/qa/service/cluster_provider/gcloud.rb b/qa/qa/service/cluster_provider/gcloud.rb new file mode 100644 index 00000000000..9c82151666c --- /dev/null +++ b/qa/qa/service/cluster_provider/gcloud.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module QA + module Service + module ClusterProvider + class Gcloud < Base + def validate_dependencies + find_executable('gcloud') || raise("You must first install `gcloud` executable to run these tests.") + end + + def set_credentials(admin_user) + master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --region #{Runtime::Env.gcloud_region} --format 'json(masterAuth.username, masterAuth.password)'`) + + shell <<~CMD.tr("\n", ' ') + kubectl config set-credentials #{admin_user} + --username #{master_auth['masterAuth']['username']} + --password #{master_auth['masterAuth']['password']} + CMD + end + + def setup + login_if_not_already_logged_in + create_cluster + end + + def teardown + delete_cluster + end + + private + + def login_if_not_already_logged_in + if Runtime::Env.has_gcloud_credentials? + attempt_login_with_env_vars + else + account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"` + if account.empty? + raise "Failed to login to gcloud. No credentials provided in environment and no credentials found locally." + else + puts "gcloud account found. Using: #{account} for creating K8s cluster." + end + end + end + + def attempt_login_with_env_vars + puts "No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY." + gcloud_account_key = Tempfile.new('gcloud-account-key') + gcloud_account_key.write(Runtime::Env.gcloud_account_key) + gcloud_account_key.close + gcloud_account_email = Runtime::Env.gcloud_account_email + shell("gcloud auth activate-service-account #{gcloud_account_email} --key-file #{gcloud_account_key.path}") + ensure + gcloud_account_key && gcloud_account_key.unlink + end + + def auth_options + "--enable-legacy-authorization" unless rbac + end + + def create_cluster + shell <<~CMD.tr("\n", ' ') + gcloud container clusters + create #{cluster_name} + #{auth_options} + --enable-basic-auth + --region #{Runtime::Env.gcloud_region} + --disk-size 10GB + --num-nodes #{Runtime::Env.gcloud_num_nodes} + && gcloud container clusters + get-credentials + --region #{Runtime::Env.gcloud_region} + #{cluster_name} + CMD + end + + def delete_cluster + shell <<~CMD.tr("\n", ' ') + gcloud container clusters delete + --region #{Runtime::Env.gcloud_region} + #{cluster_name} + --quiet --async + CMD + end + end + end + end +end diff --git a/qa/qa/service/cluster_provider/k3d.rb b/qa/qa/service/cluster_provider/k3d.rb new file mode 100644 index 00000000000..8e117c2dbd5 --- /dev/null +++ b/qa/qa/service/cluster_provider/k3d.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module QA + module Service + module ClusterProvider + class K3d < Base + def validate_dependencies + find_executable('k3d') || raise("You must first install `k3d` executable to run these tests.") + end + + def set_credentials(admin_user) + end + + def setup + shell "k3d create --workers 1 --name #{cluster_name} --wait 0" + + @old_kubeconfig = ENV['KUBECONFIG'] + ENV['KUBECONFIG'] = fetch_kubeconfig + raise "Could not fetch kubeconfig" unless ENV['KUBECONFIG'] + + install_local_storage + end + + def teardown + ENV['KUBECONFIG'] = @old_kubeconfig + shell "k3d delete --name #{cluster_name}" + end + + # Fetch "real" certificate + # See https://github.com/rancher/k3s/issues/27 + def filter_credentials(credentials) + kubeconfig = YAML.load_file(ENV['KUBECONFIG']) + ca_certificate = kubeconfig.dig('clusters', 0, 'cluster', 'certificate-authority-data') + + credentials.merge('data' => credentials['data'].merge('ca.crt' => ca_certificate)) + end + + private + + def retry_until(max_attempts: 10, wait: 1) + max_attempts.times do + result = yield + return result if result + + sleep wait + end + + raise "Retried #{max_attempts} times. Aborting" + end + + def fetch_kubeconfig + retry_until do + config = `k3d get-kubeconfig --name #{cluster_name}`.chomp + config if config =~ /kubeconfig.yaml/ + end + end + + def install_local_storage + shell('kubectl apply -f -', stdin_data: local_storage_config) + end + + # See https://github.com/rancher/k3d/issues/67 + def local_storage_config + <<~YAML + --- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: storage-provisioner + namespace: kube-system + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: storage-provisioner + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:persistent-volume-provisioner + subjects: + - kind: ServiceAccount + name: storage-provisioner + namespace: kube-system + --- + apiVersion: v1 + kind: Pod + metadata: + name: storage-provisioner + namespace: kube-system + spec: + serviceAccountName: storage-provisioner + tolerations: + - effect: NoExecute + key: node.kubernetes.io/not-ready + operator: Exists + tolerationSeconds: 300 + - effect: NoExecute + key: node.kubernetes.io/unreachable + operator: Exists + tolerationSeconds: 300 + hostNetwork: true + containers: + - name: storage-provisioner + image: gcr.io/k8s-minikube/storage-provisioner:v1.8.1 + command: ["/storage-provisioner"] + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /tmp + name: tmp + volumes: + - name: tmp + hostPath: + path: /tmp + type: Directory + --- + kind: StorageClass + apiVersion: storage.k8s.io/v1 + metadata: + name: standard + namespace: kube-system + annotations: + storageclass.kubernetes.io/is-default-class: "true" + labels: + addonmanager.kubernetes.io/mode: EnsureExists + provisioner: k8s.io/minikube-hostpath + YAML + end + end + end + end +end diff --git a/qa/qa/service/cluster_provider/minikube.rb b/qa/qa/service/cluster_provider/minikube.rb new file mode 100644 index 00000000000..fc916245357 --- /dev/null +++ b/qa/qa/service/cluster_provider/minikube.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module QA + module Service + module ClusterProvider + class Minikube < Base + def validate_dependencies + find_executable('minikube') || raise("You must first install `minikube` executable to run these tests.") + end + + def set_credentials(admin_user) + end + + def setup + shell 'minikube stop' + shell "minikube profile #{cluster_name}" + shell 'minikube start' + end + + def teardown + shell 'minikube delete' + end + end + end + end +end diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb index ac0b6313167..26b5f58d2d3 100644 --- a/qa/qa/service/kubernetes_cluster.rb +++ b/qa/qa/service/kubernetes_cluster.rb @@ -9,90 +9,63 @@ module QA class KubernetesCluster include Service::Shellout - attr_reader :api_url, :ca_certificate, :token, :rbac + attr_reader :api_url, :ca_certificate, :token, :rbac, :provider - def initialize(rbac: true) + def initialize(rbac: true, provider_class: QA::Service::ClusterProvider::Gcloud) @rbac = rbac - end - - def cluster_name - @cluster_name ||= "qa-cluster-#{SecureRandom.hex(4)}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}" + @provider = provider_class.new(rbac: rbac) end def create! validate_dependencies - login_if_not_already_logged_in - - shell <<~CMD.tr("\n", ' ') - gcloud container clusters - create #{cluster_name} - #{auth_options} - --enable-basic-auth - --region #{Runtime::Env.gcloud_region} - --disk-size 10GB - --num-nodes #{Runtime::Env.gcloud_num_nodes} - && gcloud container clusters - get-credentials - --region #{Runtime::Env.gcloud_region} - #{cluster_name} - CMD - - @api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'` - - @admin_user = "#{cluster_name}-admin" - master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --region #{Runtime::Env.gcloud_region} --format 'json(masterAuth.username, masterAuth.password)'`) - shell <<~CMD.tr("\n", ' ') - kubectl config set-credentials #{@admin_user} - --username #{master_auth['masterAuth']['username']} - --password #{master_auth['masterAuth']['password']} - CMD - - if rbac - create_service_account - - secrets = JSON.parse(`kubectl get secrets -o json`) - gitlab_account = secrets['items'].find do |item| - item['metadata']['annotations']['kubernetes.io/service-account.name'] == 'gitlab-account' - end - - @ca_certificate = Base64.decode64(gitlab_account['data']['ca.crt']) - @token = Base64.decode64(gitlab_account['data']['token']) - else - @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`) - @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`) - end + + @provider.validate_dependencies + @provider.setup + + @api_url = fetch_api_url + + credentials = @provider.filter_credentials(fetch_credentials) + @ca_certificate = Base64.decode64(credentials.dig('data', 'ca.crt')) + @token = Base64.decode64(credentials.dig('data', 'token')) self end def remove! - shell <<~CMD.tr("\n", ' ') - gcloud container clusters delete - --region #{Runtime::Env.gcloud_region} - #{cluster_name} - --quiet --async - CMD + @provider.teardown + end + + def cluster_name + @provider.cluster_name end private - def create_service_account - shell('kubectl create -f -', stdin_data: service_account) - shell("kubectl --user #{@admin_user} create -f -", stdin_data: service_account_role_binding) + def fetch_api_url + `kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'` + end + + def fetch_credentials + return global_credentials unless rbac + + @provider.set_credentials(admin_user) + create_service_account(admin_user) + account_credentials end - def service_account - <<~YAML + def admin_user + @admin_user ||= "#{@provider.cluster_name}-admin" + end + + def create_service_account(user) + service_account = <<~YAML + --- apiVersion: v1 kind: ServiceAccount metadata: name: gitlab-account namespace: default - YAML - end - - def service_account_role_binding - <<~YAML + --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: @@ -106,39 +79,24 @@ module QA name: cluster-admin apiGroup: rbac.authorization.k8s.io YAML - end - def auth_options - "--enable-legacy-authorization" unless rbac + shell('kubectl apply -f -', stdin_data: service_account) end - def validate_dependencies - find_executable('gcloud') || raise("You must first install `gcloud` executable to run these tests.") - find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.") - end + def account_credentials + secrets = JSON.parse(`kubectl get secrets -o json`) - def login_if_not_already_logged_in - if Runtime::Env.has_gcloud_credentials? - attempt_login_with_env_vars - else - account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"` - if account.empty? - raise "Failed to login to gcloud. No credentials provided in environment and no credentials found locally." - else - puts "gcloud account found. Using: #{account} for creating K8s cluster." - end + secrets['items'].find do |item| + item['metadata']['annotations']['kubernetes.io/service-account.name'] == 'gitlab-account' end end - def attempt_login_with_env_vars - puts "No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY." - gcloud_account_key = Tempfile.new('gcloud-account-key') - gcloud_account_key.write(Runtime::Env.gcloud_account_key) - gcloud_account_key.close - gcloud_account_email = Runtime::Env.gcloud_account_email - shell("gcloud auth activate-service-account #{gcloud_account_email} --key-file #{gcloud_account_key.path}") - ensure - gcloud_account_key && gcloud_account_key.unlink + def global_credentials + JSON.parse(`kubectl get secrets -o jsonpath='{.items[0]}'`) + end + + def validate_dependencies + find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.") end end end diff --git a/qa/qa/service/runner.rb b/qa/qa/service/runner.rb index 03b234f7a56..6fc5984b12a 100644 --- a/qa/qa/service/runner.rb +++ b/qa/qa/service/runner.rb @@ -7,13 +7,25 @@ module QA class Runner include Service::Shellout - attr_accessor :token, :address, :tags, :image + attr_accessor :token, :address, :tags, :image, :run_untagged + attr_writer :config def initialize(name) @image = 'gitlab/gitlab-runner:alpine' @name = name || "qa-runner-#{SecureRandom.hex(4)}" @network = Runtime::Scenario.attributes[:network] || 'test' @tags = %w[qa test] + @run_untagged = false + end + + def config + @config ||= <<~END + concurrent = 1 + check_interval = 0 + + [session_server] + session_timeout = 1800 + END end def network @@ -32,19 +44,30 @@ module QA shell <<~CMD.tr("\n", ' ') docker run -d --rm --entrypoint=/bin/sh --network #{network} --name #{@name} + -p 8093:8093 -e CI_SERVER_URL=#{@address} -e REGISTER_NON_INTERACTIVE=true -e REGISTRATION_TOKEN=#{@token} -e RUNNER_EXECUTOR=shell -e RUNNER_TAG_LIST=#{@tags.join(',')} -e RUNNER_NAME=#{@name} - #{@image} -c 'gitlab-runner register && gitlab-runner run' + #{@image} -c "#{register_command}" CMD end def remove! shell "docker rm -f #{@name}" end + + private + + def register_command + <<~CMD + printf '#{config.chomp.gsub(/\n/, "\\n").gsub('"', '\"')}' > /etc/gitlab-runner/config.toml && + gitlab-runner register --run-untagged=#{@run_untagged} && + gitlab-runner run + CMD + end end end end diff --git a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb new file mode 100644 index 00000000000..44c5e0b4196 --- /dev/null +++ b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module QA + context 'Manage with IP rate limits', :requires_admin do + describe 'Users API' do + before(:context) do + @api_client = Runtime::API::Client.new(:gitlab, ip_limits: true) + end + + let(:request) { Runtime::API::Request.new(@api_client, '/users') } + + it 'GET /users' do + 5.times do + get request.url + expect_status(200) + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb index f51c16f472c..1c1f552e224 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb @@ -19,7 +19,7 @@ module QA page.add_member(user.username) end - expect(page).to have_content(/#{user.name} (. )?@#{user.username} Given access/) + expect(page).to have_content(/@#{user.username}(\n| )?Given access/) end end end diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb new file mode 100644 index 00000000000..7b6a3579af0 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module QA + context 'Plan' do + describe 'Close issue' do + let(:issue_title) { 'issue title' } + let(:commit_message) { 'Closes' } + + before do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.perform(&:sign_in_using_credentials) + + issue = Resource::Issue.fabricate_via_api! do |issue| + issue.title = issue_title + end + + @project = issue.project + @issue_id = issue.api_response[:iid] + + # Initial commit should be pushed because + # the very first commit to the project doesn't close the issue + # https://gitlab.com/gitlab-org/gitlab-ce/issues/38965 + push_commit('Initial commit') + end + + it 'user closes an issue by pushing commit' do + push_commit("#{commit_message} ##{@issue_id}", false) + + @project.visit! + Page::Project::Show.perform do |show| + show.click_commit(commit_message) + end + commit_sha = Page::Project::Commit::Show.perform(&:commit_sha) + + Page::Project::Menu.perform(&:click_issues) + Page::Project::Issue::Index.perform do |index| + index.click_closed_issues_link + index.click_issue_link(issue_title) + end + + Page::Project::Issue::Show.perform do |show| + expect(show).to have_element(:reopen_issue_button) + expect(show).to have_content("closed via commit #{commit_sha}") + end + end + + def push_commit(commit_message, new_branch = true) + Resource::Repository::ProjectPush.fabricate! do |push| + push.commit_message = commit_message + push.new_branch = new_branch + push.file_content = commit_message + push.project = @project + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb index 317e31feea8..cdb85902758 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module QA - context 'Plan' do + # Failure issue https://gitlab.com/gitlab-org/quality/staging/issues/68 + context 'Plan', :quarantine do describe 'filter issue comments activities' do let(:issue_title) { 'issue title' } diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb index f915d412bf3..21785ca3ed3 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb @@ -49,7 +49,7 @@ module QA Page::Project::Commit::Show.perform(&:select_email_patches) - expect(page).to have_content("From: #{@user.name} <#{@user.public_email}>") + expect(page).to have_content(/From: "?#{Regexp.escape(@user.name)}"? <#{@user.public_email}>/) expect(page).to have_content('Subject: [PATCH] Add second file') expect(page).to have_content('diff --git a/second b/second') end 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 9ff7919f199..c09c65a57a5 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,8 +1,7 @@ # frozen_string_literal: true module QA - context 'Create', :quarantine do - # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/127 + context 'Create' do describe 'Web IDE file templates' do include Runtime::Fixtures diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb index 33744236dd4..900ddcb7f59 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb @@ -15,11 +15,11 @@ module QA Resource::Runner.fabricate! do |runner| runner.name = executor - end + end.project.visit! + Page::Project::Menu.perform(&:go_to_ci_cd_settings) Page::Project::Settings::CICD.perform do |settings| sleep 5 # Runner should register within 5 seconds - settings.refresh settings.expand_runners_settings do |page| expect(page).to have_content(executor) 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 f6411d8c5ad..141166f6971 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 @@ -17,7 +17,7 @@ module QA @repository_location = @project.repository_ssh_location - Resource::Runner.fabricate_via_browser_ui! do |resource| + Resource::Runner.fabricate_via_api! do |resource| resource.project = @project resource.name = @runner_name resource.tags = %w[qa docker] diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb index 60c1e105ae6..3f99ae644c7 100644 --- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb +++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb @@ -147,23 +147,31 @@ module QA end describe 'Auto DevOps', :smoke do - it 'enables AutoDevOps by default' do + before do login - project = Resource::Project.fabricate! do |p| - p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops' + @project = Resource::Project.fabricate_via_browser_ui! do |p| + p.name = "project-with-autodevops-#{SecureRandom.hex(8)}" p.description = 'Project with AutoDevOps' end + Page::Project::Menu.perform(&:go_to_ci_cd_settings) + Page::Project::Settings::CICD.perform(&:expand_auto_devops) + Page::Project::Settings::AutoDevops.perform(&:enable_autodevops) + + @project.visit! + # Create AutoDevOps repo Resource::Repository::ProjectPush.fabricate! do |push| - push.project = project + push.project = @project push.directory = Pathname .new(__dir__) .join('../../../../../fixtures/auto_devops_rack') push.commit_message = 'Create AutoDevOps compatible Project' end + end + it 'runs an AutoDevOps pipeline' do Page::Project::Menu.perform(&:click_ci_cd_pipelines) Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) |