summaryrefslogtreecommitdiff
path: root/qa/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa/qa')
-rw-r--r--qa/qa/flow/login.rb38
-rw-r--r--qa/qa/page/admin/overview/users/show.rb10
-rw-r--r--qa/qa/page/admin/settings/component/outbound_requests.rb33
-rw-r--r--qa/qa/page/admin/settings/network.rb7
-rw-r--r--qa/qa/page/base.rb52
-rw-r--r--qa/qa/page/component/select2.rb8
-rw-r--r--qa/qa/page/dashboard/projects.rb4
-rw-r--r--qa/qa/page/dashboard/welcome.rb17
-rw-r--r--qa/qa/page/element.rb10
-rw-r--r--qa/qa/page/file/shared/commit_button.rb4
-rw-r--r--qa/qa/page/group/show.rb10
-rw-r--r--qa/qa/page/main/menu.rb2
-rw-r--r--qa/qa/page/project/issue/index.rb4
-rw-r--r--qa/qa/page/project/issue/show.rb14
-rw-r--r--qa/qa/page/project/pipeline/index.rb12
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb2
-rw-r--r--qa/qa/page/project/sub_menus/settings.rb9
-rw-r--r--qa/qa/resource/base.rb11
-rw-r--r--qa/qa/resource/group.rb5
-rw-r--r--qa/qa/resource/issue.rb4
-rw-r--r--qa/qa/resource/members.rb4
-rw-r--r--qa/qa/resource/merge_request.rb2
-rw-r--r--qa/qa/resource/project.rb5
-rw-r--r--qa/qa/resource/runner.rb1
-rw-r--r--qa/qa/resource/sandbox.rb2
-rw-r--r--qa/qa/resource/user.rb19
-rw-r--r--qa/qa/runtime/api/client.rb19
-rw-r--r--qa/qa/runtime/browser.rb20
-rw-r--r--qa/qa/runtime/env.rb4
-rw-r--r--qa/qa/runtime/feature.rb40
-rw-r--r--qa/qa/runtime/fixtures.rb2
-rw-r--r--qa/qa/runtime/user.rb4
-rw-r--r--qa/qa/scenario/shared_attributes.rb1
-rw-r--r--qa/qa/service/docker_run/jenkins.rb43
-rw-r--r--qa/qa/service/docker_run/maven.rb44
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb57
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb11
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb22
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb19
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb26
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb9
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb17
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb2
-rw-r--r--qa/qa/specs/loop_runner.rb21
-rw-r--r--qa/qa/specs/runner.rb2
-rw-r--r--qa/qa/support/api.rb16
-rw-r--r--qa/qa/vendor/github/page/login.rb10
-rw-r--r--qa/qa/vendor/jenkins/page/base.rb24
-rw-r--r--qa/qa/vendor/jenkins/page/configure.rb48
-rw-r--r--qa/qa/vendor/jenkins/page/configure_job.rb62
-rw-r--r--qa/qa/vendor/jenkins/page/login.rb31
-rw-r--r--qa/qa/vendor/jenkins/page/new_credentials.rb50
-rw-r--r--qa/qa/vendor/jenkins/page/new_job.rb38
-rw-r--r--qa/qa/vendor/saml_idp/page/login.rb16
65 files changed, 854 insertions, 136 deletions
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
new file mode 100644
index 00000000000..d84dfaa9377
--- /dev/null
+++ b/qa/qa/flow/login.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module QA
+ module Flow
+ module Login
+ module_function
+
+ def while_signed_in(as: nil)
+ Page::Main::Menu.perform(&:sign_out_if_signed_in)
+
+ sign_in(as: as)
+
+ yield
+
+ Page::Main::Menu.perform(&:sign_out)
+ end
+
+ def while_signed_in_as_admin
+ while_signed_in(as: Runtime::User.admin) do
+ yield
+ end
+ end
+
+ def sign_in(as: nil)
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: as) }
+ end
+
+ def sign_in_as_admin
+ sign_in(as: Runtime::User.admin)
+ end
+
+ def sign_in_unless_signed_in(as: nil)
+ sign_in(as: as) unless Page::Main::Menu.perform(&:signed_in?)
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/admin/overview/users/show.rb b/qa/qa/page/admin/overview/users/show.rb
index 11ea7bcabc8..f15ef0492fc 100644
--- a/qa/qa/page/admin/overview/users/show.rb
+++ b/qa/qa/page/admin/overview/users/show.rb
@@ -10,9 +10,19 @@ module QA
element :impersonate_user_link
end
+ view 'app/views/admin/users/show.html.haml' do
+ element :confirm_user_button
+ end
+
def click_impersonate_user
click_element(:impersonate_user_link)
end
+
+ def confirm_user
+ accept_confirm do
+ click_element :confirm_user_button
+ end
+ end
end
end
end
diff --git a/qa/qa/page/admin/settings/component/outbound_requests.rb b/qa/qa/page/admin/settings/component/outbound_requests.rb
new file mode 100644
index 00000000000..248ea5b6715
--- /dev/null
+++ b/qa/qa/page/admin/settings/component/outbound_requests.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Admin
+ module Settings
+ module Component
+ class OutboundRequests < Page::Base
+ view 'app/views/admin/application_settings/_outbound.html.haml' do
+ element :allow_requests_from_services_checkbox
+ element :save_changes_button
+ end
+
+ def allow_requests_to_local_network_from_services
+ check_allow_requests_to_local_network_from_services_checkbox
+ click_save_changes_button
+ end
+
+ private
+
+ def check_allow_requests_to_local_network_from_services_checkbox
+ check_element :allow_requests_from_services_checkbox
+ end
+
+ def click_save_changes_button
+ 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
index fdb8fcda281..83566d3d1ca 100644
--- a/qa/qa/page/admin/settings/network.rb
+++ b/qa/qa/page/admin/settings/network.rb
@@ -9,6 +9,7 @@ module QA
view 'app/views/admin/application_settings/network.html.haml' do
element :ip_limits_section
+ element :outbound_requests_section
end
def expand_ip_limits(&block)
@@ -16,6 +17,12 @@ module QA
Component::IpLimits.perform(&block)
end
end
+
+ def expand_outbound_requests(&block)
+ expand_section(:outbound_requests_section) do
+ Component::OutboundRequests.perform(&block)
+ end
+ end
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 71df90f2f42..ed4d33dc7a3 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -111,12 +111,18 @@ module QA
element.select value
end
- def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
- has_css?(element_selector_css(name), wait: wait, text: text)
+ def has_element?(name, **kwargs)
+ wait = kwargs[:wait] ? kwargs[:wait] && kwargs.delete(:wait) : Capybara.default_max_wait_time
+ text = kwargs[:text] ? kwargs[:text] && kwargs.delete(:text) : nil
+
+ has_css?(element_selector_css(name, kwargs), text: text, wait: wait)
end
- def has_no_element?(name, text: nil, wait: Capybara.default_max_wait_time)
- has_no_css?(element_selector_css(name), wait: wait, text: text)
+ def has_no_element?(name, **kwargs)
+ wait = kwargs[:wait] ? kwargs[:wait] && kwargs.delete(:wait) : Capybara.default_max_wait_time
+ text = kwargs[:text] ? kwargs[:text] && kwargs.delete(:text) : nil
+
+ has_no_css?(element_selector_css(name, kwargs), wait: wait, text: text)
end
def has_text?(text)
@@ -135,6 +141,40 @@ module QA
has_no_css?('.fa-spinner.block-loading', wait: Capybara.default_max_wait_time)
end
+ def has_loaded_all_images?
+ # I don't know of a foolproof way to wait for all images to load
+ # This loop gives time for the img tags to be rendered and for
+ # images to start loading.
+ previous_total_images = 0
+ wait(interval: 1) do
+ current_total_images = all("img").size
+ result = previous_total_images == current_total_images
+ previous_total_images = current_total_images
+ result
+ end
+
+ # Retry until all images found can be fetched via HTTP, and
+ # check that the image has a non-zero natural width (a broken
+ # img tag could have a width, but wouldn't have a natural width)
+
+ # Unfortunately, this doesn't account for SVGs. They're rendered
+ # as HTML, so there doesn't seem to be a way to check that they
+ # display properly via Selenium. However, if the SVG couldn't be
+ # rendered (e.g., because the file doesn't exist), the whole page
+ # won't display properly, so we should catch that with the test
+ # this method is called from.
+
+ # The user's avatar is an img, which could be a gravatar image,
+ # so we skip that by only checking for images hosted internally
+ retry_until(sleep_interval: 1) do
+ all("img").all? do |image|
+ next true unless URI(image['src']).host == URI(page.current_url).host
+
+ asset_exists?(image['src']) && image['naturalWidth'].to_i > 0
+ end
+ end
+ end
+
def wait_for_animated_element(name)
# It would be ideal if we could detect when the animation is complete
# but in some cases there's nothing we can easily access via capybara
@@ -165,8 +205,8 @@ module QA
scroll_to(element_selector_css(name), *args)
end
- def element_selector_css(name)
- Page::Element.new(name).selector_css
+ def element_selector_css(name, *attributes)
+ Page::Element.new(name, *attributes).selector_css
end
def click_link_with_text(text)
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
index d05c44d22b2..8fe6a4a75b3 100644
--- a/qa/qa/page/component/select2.rb
+++ b/qa/qa/page/component/select2.rb
@@ -20,12 +20,20 @@ module QA
def search_and_select(item_text)
find('.select2-input').set(item_text)
+
+ wait_for_search_to_complete
+
select_item(item_text)
end
def expand_select_list
find('span.select2-arrow').click
end
+
+ def wait_for_search_to_complete
+ has_css?('.select2-active')
+ has_no_css?('.select2-active', wait: 30)
+ end
end
end
end
diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb
index 378ac793f7b..c103bc26a36 100644
--- a/qa/qa/page/dashboard/projects.rb
+++ b/qa/qa/page/dashboard/projects.rb
@@ -18,6 +18,10 @@ module QA
'/'
end
+ def clear_project_filter
+ fill_element(:project_filter_form, "")
+ end
+
private
def filter_by_name(name)
diff --git a/qa/qa/page/dashboard/welcome.rb b/qa/qa/page/dashboard/welcome.rb
new file mode 100644
index 00000000000..b54205780d9
--- /dev/null
+++ b/qa/qa/page/dashboard/welcome.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Dashboard
+ class Welcome < Page::Base
+ view 'app/views/dashboard/projects/_zero_authorized_projects.html.haml' do
+ element :welcome_title_content
+ end
+
+ def has_welcome_title?(text)
+ has_element?(:welcome_title_content, text: text)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/element.rb b/qa/qa/page/element.rb
index 9e6fd2fdd4f..6bfdf98587b 100644
--- a/qa/qa/page/element.rb
+++ b/qa/qa/page/element.rb
@@ -28,7 +28,7 @@ module QA
end
def selector_css
- %Q([data-qa-selector="#{@name}"],.#{selector})
+ %Q([data-qa-selector="#{@name}"]#{additional_selectors},.#{selector})
end
def expression
@@ -42,6 +42,14 @@ module QA
def matches?(line)
!!(line =~ /["']#{name}['"]|#{expression}/)
end
+
+ private
+
+ def additional_selectors
+ @attributes.dup.delete_if { |attr| attr == :pattern || attr == :required }.map do |key, value|
+ %Q([data-qa-#{key.to_s.tr('_', '-')}="#{value}"])
+ end.join
+ end
end
end
end
diff --git a/qa/qa/page/file/shared/commit_button.rb b/qa/qa/page/file/shared/commit_button.rb
index d8e751dd7b6..559b4c6ceea 100644
--- a/qa/qa/page/file/shared/commit_button.rb
+++ b/qa/qa/page/file/shared/commit_button.rb
@@ -13,6 +13,10 @@ module QA
def commit_changes
click_element(:commit_button)
+
+ wait(reload: false, max: 60) do
+ finished_loading?
+ end
end
end
end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index d4c4be0d6ca..e1f319da134 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -18,6 +18,10 @@ module QA
element :no_result_text, 'No groups or projects matched your search' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/shared/members/_access_request_links.html.haml' do
+ element :leave_group_link
+ end
+
def click_subgroup(name)
click_link name
end
@@ -42,6 +46,12 @@ module QA
click_element :new_in_group_button
end
+ def leave_group
+ accept_alert do
+ click_element :leave_group_link
+ end
+ end
+
private
def select_kind(kind)
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 024f56db8e2..49c48568e68 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -20,7 +20,7 @@ module QA
element :admin_area_link
element :projects_dropdown, required: true
element :groups_dropdown, required: true
- element :more_dropdown, required: true
+ element :more_dropdown
element :snippets_link
end
diff --git a/qa/qa/page/project/issue/index.rb b/qa/qa/page/project/issue/index.rb
index befee25b37a..a6ccee4353b 100644
--- a/qa/qa/page/project/issue/index.rb
+++ b/qa/qa/page/project/issue/index.rb
@@ -36,6 +36,10 @@ module QA
def click_closed_issues_link
click_element :closed_issues_link
end
+
+ def has_issue?(issue)
+ has_element? :issue, issue_title: issue.to_s
+ end
end
end
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index d2732eb7dd2..6ec80b7c9cc 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -108,6 +108,10 @@ module QA
find_element(:more_assignees_link)
end
+ def noteable_note_item
+ find_element(:noteable_note_item)
+ end
+
def select_all_activities_filter
select_filter_with_text('Show all activity')
end
@@ -161,7 +165,15 @@ module QA
def select_user(username)
find("#{element_selector_css(:assignee_block)} input").set(username)
- find('.dropdown-menu-user-link', text: "@#{username}").click
+
+ dropdown_menu_user_link_selector = '.dropdown-menu-user-link'
+ at_username = "@#{username}"
+ ten_seconds = 10
+
+ wait(reload: false, max: ten_seconds, interval: 1) do
+ has_css?(dropdown_menu_user_link_selector, wait: ten_seconds, text: at_username)
+ end
+ find(dropdown_menu_user_link_selector, text: at_username).click
end
def wait_assignees_block_finish_loading
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index fae7818f871..b52f3e99a36 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -7,6 +7,10 @@ module QA::Page
element :pipeline_link, 'class="js-pipeline-url-link' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/assets/javascripts/pipelines/components/pipelines_table_row.vue' do
+ element :pipeline_commit_status
+ end
+
def click_on_latest_pipeline
css = '.js-pipeline-url-link'
@@ -16,6 +20,14 @@ module QA::Page
link.click
end
+
+ def wait_for_latest_pipeline_success
+ wait(reload: false, max: 300) do
+ within_element_by_index(:pipeline_commit_status, 0) do
+ has_text?('passed')
+ 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 45040cf4660..46f93fad61e 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -35,3 +35,5 @@ module QA
end
end
end
+
+QA::Page::Project::Settings::CICD.prepend_if_ee('QA::EE::Page::Project::Settings::CICD')
diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb
index 1cd39fcff58..8be442ba35d 100644
--- a/qa/qa/page/project/sub_menus/settings.rb
+++ b/qa/qa/page/project/sub_menus/settings.rb
@@ -13,6 +13,7 @@ module QA
element :settings_item
element :link_members_settings
element :general_settings_link
+ element :integrations_settings_link
end
end
end
@@ -55,6 +56,14 @@ module QA
end
end
+ def go_to_integrations_settings
+ hover_settings do
+ within_submenu do
+ click_element :integrations_settings_link
+ end
+ end
+ end
+
private
def hover_settings
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index 88069df6ade..ae20ca1a98e 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -64,7 +64,12 @@ module QA
end
def visit!
- visit(web_url)
+ Runtime::Logger.debug(%Q[Visiting #{self.class.name} at "#{web_url}"]) if Runtime::Env.debug?
+
+ Support::Retrier.retry_until do
+ visit(web_url)
+ wait { current_url.include?(URI.parse(web_url).path.split('/').last || web_url) }
+ end
end
def populate(*attributes)
@@ -72,7 +77,9 @@ module QA
end
def wait(max: 60, interval: 0.1)
- QA::Support::Waiter.wait(max: max, interval: interval)
+ QA::Support::Waiter.wait(max: max, interval: interval) do
+ yield
+ end
end
private
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index e11bd5728fb..7511396251d 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -8,7 +8,10 @@ module QA
attr_accessor :path, :description
attribute :sandbox do
- Sandbox.fabricate!
+ Sandbox.fabricate_via_api! do |sandbox|
+ sandbox.user = user
+ sandbox.api_client = api_client
+ end
end
attribute :id
diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb
index 0817a9de06f..3bcff6a10ac 100644
--- a/qa/qa/resource/issue.rb
+++ b/qa/qa/resource/issue.rb
@@ -38,6 +38,10 @@ module QA
end
end
+ def to_s
+ @title
+ end
+
def api_get_path
"/projects/#{project.id}/issues/#{id}"
end
diff --git a/qa/qa/resource/members.rb b/qa/qa/resource/members.rb
index d70a2907523..c738a91a77f 100644
--- a/qa/qa/resource/members.rb
+++ b/qa/qa/resource/members.rb
@@ -11,6 +11,10 @@ module QA
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
+ def list_members
+ JSON.parse(get(Runtime::API::Request.new(api_client, api_members_path).url).body)
+ end
+
def api_members_path
"#{api_get_path}/members"
end
diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb
index fe7eeeed37a..1a6de8de456 100644
--- a/qa/qa/resource/merge_request.rb
+++ b/qa/qa/resource/merge_request.rb
@@ -26,8 +26,6 @@ module QA
end
attribute :target do
- project.visit!
-
Repository::ProjectPush.fabricate! do |resource|
resource.project = project
resource.branch_name = 'master'
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index caaa766e982..3bebe2aaeda 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -9,6 +9,7 @@ module QA
include Members
attr_writer :initialize_with_readme
+ attr_writer :auto_devops_enabled
attr_writer :visibility
attribute :id
@@ -47,6 +48,7 @@ module QA
@standalone = false
@description = 'My awesome project'
@initialize_with_readme = false
+ @auto_devops_enabled = true
@visibility = 'public'
end
@@ -101,7 +103,8 @@ module QA
name: name,
description: description,
visibility: @visibility,
- initialize_with_readme: @initialize_with_readme
+ initialize_with_readme: @initialize_with_readme,
+ auto_devops_enabled: @auto_devops_enabled
}
unless @standalone
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index 1be2429bc04..102c1ec83f5 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -36,7 +36,6 @@ module QA
runner.tags = tags
runner.image = image
runner.config = config if config
- runner.run_untagged = true
runner.register!
end
end
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 6ee3dcf350f..6c87fcb377a 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -7,6 +7,8 @@ module QA
# creating it if it doesn't yet exist.
#
class Sandbox < Base
+ include Members
+
attr_accessor :path
attribute :id
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index dcf145c9882..bdbe5f3ef51 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -7,16 +7,21 @@ module QA
class User < Base
attr_reader :unique_id
attr_writer :username, :password
- attr_accessor :provider, :extern_uid
+ attr_accessor :admin, :provider, :extern_uid
attribute :id
attribute :name
attribute :email
def initialize
+ @admin = false
@unique_id = SecureRandom.hex(8)
end
+ def admin?
+ api_resource&.dig(:is_admin) || false
+ end
+
def username
@username || "qa-user-#{unique_id}"
end
@@ -71,6 +76,16 @@ module QA
super
end
+ def api_delete
+ super
+
+ QA::Runtime::Logger.debug("Deleted user '#{username}'") if Runtime::Env.debug?
+ end
+
+ def api_delete_path
+ "/users/#{id}"
+ end
+
def api_get_path
"/users/#{fetch_id(username)}"
end
@@ -81,6 +96,7 @@ module QA
def api_post_body
{
+ admin: admin,
email: email,
password: password,
username: username,
@@ -93,6 +109,7 @@ module QA
if Runtime::Env.signup_disabled?
self.fabricate_via_api! do |user|
user.username = username
+ user.password = password
end
else
self.fabricate!
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
index 1b0adbc9053..83fbb8f15d2 100644
--- a/qa/qa/runtime/api/client.rb
+++ b/qa/qa/runtime/api/client.rb
@@ -46,19 +46,24 @@ module QA
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) }
+ signed_in_initially = Page::Main::Menu.perform(&:signed_in?)
- unless Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
- Runtime::Browser.visit(@address, Page::Main::Login)
- Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: @user) }
- end
+ Page::Main::Menu.perform(&:sign_out) if @is_new_session && signed_in_initially
+
+ Flow::Login.sign_in_unless_signed_in(as: @user)
token = Resource::PersonalAccessToken.fabricate!.access_token
# If this is a new session, that tests that follow could fail if they
- # try to sign in without starting a new session
+ # try to sign in without starting a new session.
+ # Also, if the browser wasn't already signed in, leaving it
+ # signed in could cause tests to fail when they try to sign
+ # in again. For example, that would happen if a test has a
+ # before(:context) block that fabricates via the API, and
+ # it's the first test to run so it creates an access token
+ #
# Sign out so the tests can successfully sign in
- Page::Main::Menu.perform(&:sign_out) if @is_new_session
+ Page::Main::Menu.perform(&:sign_out) if @is_new_session || !signed_in_initially
token
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index 4789b380377..7e45e5e86ea 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -19,6 +19,12 @@ module QA
self.class.configure!
end
+ def self.blank_page?
+ ['', 'about:blank', 'data:,'].include?(Capybara.current_session.driver.browser.current_url)
+ rescue
+ true
+ end
+
##
# Visit a page that belongs to a GitLab instance under given address.
#
@@ -51,13 +57,13 @@ module QA
Capybara.register_driver QA::Runtime::Env.browser do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.send(QA::Runtime::Env.browser,
- # This enables access to logs with `page.driver.manage.get_log(:browser)`
- loggingPrefs: {
- browser: "ALL",
- client: "ALL",
- driver: "ALL",
- server: "ALL"
- })
+ # This enables access to logs with `page.driver.manage.get_log(:browser)`
+ loggingPrefs: {
+ browser: "ALL",
+ client: "ALL",
+ driver: "ALL",
+ server: "ALL"
+ })
if QA::Runtime::Env.accept_insecure_certs?
capabilities['acceptInsecureCerts'] = true
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index b4047ef5088..bcd2a225469 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -261,6 +261,10 @@ module QA
ENV['QA_RUNTIME_SCENARIO_ATTRIBUTES']
end
+ def gitlab_qa_loop_runner_minutes
+ ENV.fetch('GITLAB_QA_LOOP_RUNNER_MINUTES', 1).to_i
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/runtime/feature.rb b/qa/qa/runtime/feature.rb
index b74f343ba7b..8c19436ee12 100644
--- a/qa/qa/runtime/feature.rb
+++ b/qa/qa/runtime/feature.rb
@@ -7,6 +7,7 @@ module QA
extend Support::Api
SetFeatureError = Class.new(RuntimeError)
+ AuthorizationError = Class.new(RuntimeError)
def enable(key)
QA::Runtime::Logger.info("Enabling feature: #{key}")
@@ -18,6 +19,28 @@ module QA
set_feature(key, false)
end
+ def remove(key)
+ request = Runtime::API::Request.new(api_client, "/features/#{key}")
+ response = delete(request.url)
+ unless response.code == QA::Support::Api::HTTP_STATUS_NO_CONTENT
+ raise SetFeatureError, "Deleting feature flag #{key} failed with `#{response}`."
+ end
+ end
+
+ def enable_and_verify(key)
+ Support::Retrier.retry_on_exception(sleep_interval: 2) do
+ enable(key)
+
+ is_enabled = false
+
+ QA::Support::Waiter.wait(interval: 1) do
+ is_enabled = enabled?(key)
+ end
+
+ raise SetFeatureError, "#{key} was not enabled!" unless is_enabled
+ end
+ end
+
def enabled?(key)
feature = JSON.parse(get_features).find { |flag| flag["name"] == key }
feature && feature["state"] == "on"
@@ -26,7 +49,22 @@ module QA
private
def api_client
- @api_client ||= Runtime::API::Client.new(:gitlab)
+ @api_client ||= begin
+ if Runtime::Env.admin_personal_access_token
+ Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token)
+ else
+ user = Resource::User.fabricate_via_api! do |user|
+ user.username = Runtime::User.admin_username
+ user.password = Runtime::User.admin_password
+ end
+
+ unless user.admin?
+ raise AuthorizationError, "Administrator access is required to enable/disable feature flags. User '#{user.username}' is not an administrator."
+ end
+
+ Runtime::API::Client.new(:gitlab, user: user)
+ end
+ end
end
def set_feature(key, value)
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
index f91218ea0b5..ed051b18a9a 100644
--- a/qa/qa/runtime/fixtures.rb
+++ b/qa/qa/runtime/fixtures.rb
@@ -30,7 +30,7 @@ module QA
yield dir
ensure
- FileUtils.remove_entry(dir)
+ FileUtils.remove_entry(dir, true)
end
private
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index 3c26a3ad691..c50fcc25304 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -5,6 +5,10 @@ module QA
module User
extend self
+ def admin
+ Struct.new(:username, :password).new(admin_username, admin_password)
+ end
+
def default_username
'root'
end
diff --git a/qa/qa/scenario/shared_attributes.rb b/qa/qa/scenario/shared_attributes.rb
index 52f50ec8c27..bb45c4ce4cb 100644
--- a/qa/qa/scenario/shared_attributes.rb
+++ b/qa/qa/scenario/shared_attributes.rb
@@ -8,6 +8,7 @@ module QA
attribute :gitlab_address, '--address URL', 'Address of the instance to test'
attribute :enable_feature, '--enable-feature FEATURE_FLAG', 'Enable a feature before running tests'
attribute :parallel, '--parallel', 'Execute tests in parallel'
+ attribute :loop, '--loop', 'Execute test repeatedly'
end
end
end
diff --git a/qa/qa/service/docker_run/jenkins.rb b/qa/qa/service/docker_run/jenkins.rb
new file mode 100644
index 00000000000..00b63282484
--- /dev/null
+++ b/qa/qa/service/docker_run/jenkins.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module DockerRun
+ class Jenkins < Base
+ def initialize
+ @image = 'registry.gitlab.com/gitlab-org/gitlab-qa/jenkins-gitlab:version1'
+ @name = 'jenkins-server'
+ @port = '8080'
+ super()
+ end
+
+ def host_address
+ "http://#{host_name}:#{@port}"
+ end
+
+ def host_name
+ return 'localhost' unless QA::Runtime::Env.running_in_ci?
+
+ super
+ end
+
+ def register!
+ command = <<~CMD.tr("\n", ' ')
+ docker run -d --rm
+ --network #{network}
+ --hostname #{host_name}
+ --name #{@name}
+ --env JENKINS_HOME=jenkins_home
+ --publish #{@port}:8080
+ --publish 50000:50000
+ #{@image}
+ CMD
+
+ command.gsub!("--network #{network} ", '') unless QA::Runtime::Env.running_in_ci?
+
+ shell command
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/docker_run/maven.rb b/qa/qa/service/docker_run/maven.rb
new file mode 100644
index 00000000000..8bdea20963d
--- /dev/null
+++ b/qa/qa/service/docker_run/maven.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module QA
+ module Service
+ module DockerRun
+ class Maven < Base
+ def initialize(volume_host_path)
+ @image = 'maven:3.6.2-ibmjava-8-alpine'
+ @name = "qa-maven-#{SecureRandom.hex(8)}"
+ @volume_host_path = volume_host_path
+
+ super()
+ end
+
+ def publish!
+ # When we run the tests via gitlab-qa, we use docker-in-docker
+ # which means that host of a volume mount would be the host that
+ # started the gitlab-qa QA container (e.g., the CI runner),
+ # not the gitlab-qa container itself. That means we can't
+ # mount a volume from the file system inside the gitlab-qa
+ # container.
+ #
+ # Instead, we copy the files into the container.
+ shell <<~CMD.tr("\n", ' ')
+ docker run -d --rm
+ --network #{network}
+ --hostname #{host_name}
+ --name #{@name}
+ --volume #{@volume_host_path}:/home/maven
+ #{@image} sh -c "sleep 300"
+ CMD
+ shell "docker cp #{@volume_host_path}/. #{@name}:/home/maven"
+ shell "docker exec -t #{@name} sh -c 'cd /home/maven && mvn deploy -s settings.xml'"
+
+ # Stop the container when `mvn deploy` is finished otherwise
+ # the sleeping container will hold onto the files in @volume_host_path,
+ # which causes problems when they're created in a tmp dir
+ # that we want to delete
+ shell "docker stop #{@name}"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
index 101143399f6..ad67f02eaca 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_instance_wide_saml_sso_spec.rb
@@ -8,7 +8,9 @@ module QA
Page::Main::Login.perform(&:sign_in_with_saml)
- Vendor::SAMLIdp::Page::Login.perform(&:login)
+ Vendor::SAMLIdp::Page::Login.perform do |login_page|
+ login_page.login('user1', 'user1pass')
+ end
expect(page).to have_content('Welcome to GitLab')
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
new file mode 100644
index 00000000000..6a5bc6173e0
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'nokogiri'
+
+module QA
+ context 'Manage' do
+ describe 'Check for broken images', :requires_admin do
+ before(:context) do
+ admin = QA::Resource::User.new.tap do |user|
+ user.username = QA::Runtime::User.admin_username
+ user.password = QA::Runtime::User.admin_password
+ end
+ @api_client = Runtime::API::Client.new(:gitlab, user: admin)
+ @new_user = Resource::User.fabricate_via_api! do |user|
+ user.api_client = @api_client
+ end
+ @new_admin = Resource::User.fabricate_via_api! do |user|
+ user.admin = true
+ user.api_client = @api_client
+ end
+
+ Page::Main::Menu.perform(&:sign_out_if_signed_in)
+ end
+
+ after(:context) do
+ @new_user.remove_via_api!
+ @new_admin.remove_via_api!
+ end
+
+ shared_examples 'loads all images' do
+ it 'loads all images' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: new_user) }
+
+ Page::Dashboard::Welcome.perform do |welcome|
+ expect(welcome).to have_welcome_title("Welcome to GitLab")
+
+ # This would be better if it were a visual validation test
+ expect(welcome).to have_loaded_all_images
+ end
+ end
+ end
+
+ context 'when logged in as a new user' do
+ it_behaves_like 'loads all images' do
+ let(:new_user) { @new_user }
+ end
+ end
+
+ context 'when logged in as a new admin' do
+ it_behaves_like 'loads all images' do
+ let(:new_user) { @new_admin }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
index 55e15b19200..69389672a6d 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
@@ -2,13 +2,12 @@
module QA
context 'Plan' do
- describe 'check xss occurence in @mentions in issues' do
+ describe 'check xss occurence in @mentions in issues', :requires_admin do
it 'user mentions a user in comment' do
QA::Runtime::Env.personal_access_token = QA::Runtime::Env.admin_personal_access_token
unless QA::Runtime::Env.personal_access_token
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_admin_credentials)
+ Flow::Login.sign_in_as_admin
end
user = Resource::User.fabricate_via_api! do |user|
@@ -20,9 +19,7 @@ module QA
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform { |p| p.has_personal_area?(wait: 0) }
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
-
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
project = Resource::Project.fabricate_via_api! do |resource|
resource.name = 'xss-test-for-mentions-project'
@@ -42,7 +39,7 @@ module QA
Page::Project::Issue::Show.perform do |show|
show.select_all_activities_filter
- show.comment('cc-ing you here @eve')
+ show.comment("cc-ing you here @#{user.username}")
expect do
expect(show).to have_content("cc-ing you here")
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
index 2bcc89cb338..dc7fa9f3859 100644
--- 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
@@ -7,8 +7,7 @@ module QA
let(:commit_message) { 'Closes' }
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
index ad70f6813fb..77fcc4e9b6a 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
@@ -6,8 +6,7 @@ module QA
let(:my_first_reply) { 'My first reply' }
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'issue title'
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
index 0b1bd00ac8d..77489c0ecf5 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb
@@ -4,8 +4,7 @@ module QA
context 'Plan' do
describe 'Issue comments' do
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'issue title'
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 04ae4963d3a..254efb741b3 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -6,23 +6,25 @@ module QA
let(:issue_title) { 'issue title' }
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
end
it 'user creates an issue' do
- Resource::Issue.fabricate_via_browser_ui! do |issue|
+ issue = Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.title = issue_title
end
Page::Project::Menu.perform(&:click_issues)
- expect(page).to have_content(issue_title)
+ Page::Project::Issue::Index.perform do |index|
+ expect(index).to have_issue(issue)
+ end
end
context 'when using attachments in comments', :object_storage do
+ let(:gif_file_name) { 'banana_sample.gif' }
let(:file_to_attach) do
- File.absolute_path(File.join('spec', 'fixtures', 'banana_sample.gif'))
+ File.absolute_path(File.join('spec', 'fixtures', gif_file_name))
end
before do
@@ -37,15 +39,7 @@ module QA
Page::Project::Issue::Show.perform do |show|
show.comment('See attached banana for scale', attachment: file_to_attach)
- show.refresh
-
- image_url = find('a[href$="banana_sample.gif"]')[:href]
-
- found = show.wait(reload: false) do
- show.asset_exists?(image_url)
- end
-
- expect(found).to be_truthy
+ expect(show.noteable_note_item.find("img[src$='#{gif_file_name}']")).to be_visible
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..a4f6b0bb1bf 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
@@ -6,8 +6,7 @@ module QA
let(:issue_title) { 'issue title' }
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
index c42c2cedde0..e15afd1f576 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
@@ -6,8 +6,7 @@ module QA
let(:issue_title) { 'Issue Lists are awesome' }
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
project = Resource::Project.fabricate_via_api! do |resource|
resource.name = 'project-for-issue-suggestions'
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
index 45c14d0537c..b1a80ad75cd 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb
@@ -4,8 +4,7 @@ module QA
context 'Plan', :smoke do
describe 'mention' do
before do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
+ Flow::Login.sign_in
@user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
index 891cef6c420..0eaec61b2fa 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
@@ -4,9 +4,6 @@ module QA
context 'Create' do
describe 'Download merge request patch and diff' do
before(:context) do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
-
project = Resource::Project.fabricate_via_api! do |project|
project.name = 'project'
end
@@ -19,6 +16,8 @@ module QA
end
it 'views the merge request email patches' do
+ Flow::Login.sign_in
+
@merge_request.visit!
Page::MergeRequest::Show.perform(&:view_email_patches)
@@ -28,6 +27,8 @@ module QA
end
it 'views the merge request plain diff' do
+ Flow::Login.sign_in
+
@merge_request.visit!
Page::MergeRequest::Show.perform(&:view_plain_diff)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
index e42d538fdf8..d2fd1d743fb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -1,27 +1,17 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/gitlab/issues/34551
+ context 'Create', :quarantine do
describe 'File templates' do
include Runtime::Fixtures
- def login
- unless Page::Main::Menu.perform(&:signed_in?)
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- end
- end
-
before(:all) do
- login
-
- @project = Resource::Project.fabricate! do |project|
+ @project = Resource::Project.fabricate_via_api! do |project|
project.name = 'file-template-project'
project.description = 'Add file templates via the Files view'
project.initialize_with_readme = true
end
-
- Page::Main::Menu.perform(&:sign_out)
end
templates = [
@@ -55,7 +45,8 @@ module QA
it "user adds #{template[:file_name]} via file template #{template[:name]}" do
content = fetch_template_from_api(template[:api_path], template[:api_key])
- login
+ Flow::Login.sign_in
+
@project.visit!
Page::Project::Show.perform(&:create_new_file!)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
index bb1e3ced333..3306c5f5c50 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
@@ -66,24 +66,22 @@ module QA
expect(page).to have_content(commit_message_of_second_branch)
expect(page).to have_content(commit_message_of_third_branch)
- Page::Project::Branches::Show.perform do |branches|
- expect(branches).to have_branch_with_badge(second_branch, 'merged')
- end
+ Page::Project::Branches::Show.perform do |branches_page|
+ expect(branches_page).to have_branch_with_badge(second_branch, 'merged')
- Page::Project::Branches::Show.perform do |branches_view|
- branches_view.delete_branch(third_branch)
- expect(branches_view).to have_no_branch(third_branch)
- end
+ branches_page.delete_branch(third_branch)
+
+ expect(branches_page).to have_no_branch(third_branch)
+
+ branches_page.delete_merged_branches
- Page::Project::Branches::Show.perform(&:delete_merged_branches)
+ expect(branches_page).to have_content(
+ 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
+ )
- expect(page).to have_content(
- 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
- )
+ branches_page.refresh
- page.refresh
- Page::Project::Branches::Show.perform do |branches_view|
- expect(branches_view).to have_no_branch(second_branch, reload: true)
+ expect(branches_page).to have_no_branch(second_branch, reload: true)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
index f2584f55a60..0650c8395c7 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
@@ -4,14 +4,10 @@ module QA
context 'Create' do
describe 'Git clone over HTTP', :ldap_no_tls do
before(:all) do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
-
- @project = Resource::Project.fabricate! do |scenario|
+ @project = Resource::Project.fabricate_via_api! do |scenario|
scenario.name = 'project-with-code'
scenario.description = 'project for git clone tests'
end
- @project.visit!
Git::Repository.perform do |repository|
repository.uri = @project.repository_http_location.uri
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 b2eef38f896..aee62bacfa8 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
@@ -4,9 +4,6 @@ module QA
context 'Create' do
describe 'Commit data' do
before(:context) do
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
-
# Get the user's details to confirm they're included in the email patch
@user = Resource::User.fabricate_via_api! do |user|
user.username = Runtime::User.username
@@ -34,9 +31,11 @@ module QA
end
def view_commit
+ Flow::Login.sign_in
+
@project.visit!
- Page::Project::Show.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
- page.click_commit(@commit_message)
+ Page::Project::Show.perform do |show|
+ show.click_commit(@commit_message)
end
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 0a89f0c9d41..318adc3c272 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,25 +1,17 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/gitlab/issues/34551
+ context 'Create', :quarantine do
describe 'Web IDE file templates' do
include Runtime::Fixtures
- def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- end
-
before(:all) do
- login
-
- @project = Resource::Project.fabricate! do |project|
+ @project = Resource::Project.fabricate_via_api! do |project|
project.name = 'file-template-project'
project.description = 'Add file templates via the Web IDE'
project.initialize_with_readme = true
end
-
- Page::Main::Menu.perform(&:sign_out)
end
templates = [
@@ -53,7 +45,8 @@ module QA
it "user adds #{template[:file_name]} via file template #{template[:name]}" do
content = fetch_template_from_api(template[:api_path], template[:api_key])
- login
+ Flow::Login.sign_in
+
@project.visit!
Page::Project::Show.perform(&:open_web_ide!)
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 e45ce438fc2..9dc4bcc8a03 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
@@ -6,6 +6,10 @@ module QA
context 'Release', :docker do
describe 'Git clone using a deploy key' do
before do
+ # Handle WIP Job Logs flag - https://gitlab.com/gitlab-org/gitlab/issues/31162
+ @job_log_json_flag_enabled = Runtime::Feature.enabled?('job_log_json')
+ Runtime::Feature.disable('job_log_json') if @job_log_json_flag_enabled
+
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
@@ -26,6 +30,7 @@ module QA
end
after do
+ Runtime::Feature.enable('job_log_json') if @job_log_json_flag_enabled
Service::DockerRun::GitlabRunner.new(@runner_name).remove!
end
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 3f99ae644c7..e9a3b0f75e6 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
@@ -55,7 +55,8 @@ module QA
end
end
- describe 'Auto DevOps support', :orchestrated, :kubernetes do
+ # https://gitlab.com/gitlab-org/gitlab/issues/35156
+ describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do
context 'when rbac is enabled' do
before(:all) do
@cluster = Service::KubernetesCluster.new.create!
diff --git a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
index 4fca2db3d0f..187c4a2a248 100644
--- a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
+++ b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
@@ -2,7 +2,7 @@
module QA
context 'Performance bar' do
- context 'when logged in as an admin user' do
+ context 'when logged in as an admin user', :requires_admin do
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
diff --git a/qa/qa/specs/loop_runner.rb b/qa/qa/specs/loop_runner.rb
new file mode 100644
index 00000000000..f97f5cbbd81
--- /dev/null
+++ b/qa/qa/specs/loop_runner.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Specs
+ module LoopRunner
+ module_function
+
+ def run(args)
+ start = Time.now
+ loop_duration = 60 * QA::Runtime::Env.gitlab_qa_loop_runner_minutes
+
+ while Time.now - start < loop_duration
+ RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status|
+ abort if status.nonzero?
+ end
+ RSpec.clear_examples
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index 6aa08cf77b4..ac73cc00dbf 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -63,6 +63,8 @@ module QA
if Runtime::Scenario.attributes[:parallel]
ParallelRunner.run(args.flatten)
+ elsif Runtime::Scenario.attributes[:loop]
+ LoopRunner.run(args.flatten)
else
RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status|
abort if status.nonzero?
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index d0ff1f8bc2c..cd496efb4db 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -14,7 +14,7 @@ module QA
payload: payload,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
- e.response
+ return_response_or_raise(e)
end
def get(url, raw_response: false)
@@ -24,7 +24,7 @@ module QA
verify_ssl: false,
raw_response: raw_response)
rescue RestClient::ExceptionWithResponse => e
- e.response
+ return_response_or_raise(e)
end
def put(url, payload)
@@ -34,7 +34,7 @@ module QA
payload: payload,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
- e.response
+ return_response_or_raise(e)
end
def delete(url)
@@ -43,7 +43,7 @@ module QA
url: url,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
- e.response
+ return_response_or_raise(e)
end
def head(url)
@@ -52,12 +52,18 @@ module QA
url: url,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
- e.response
+ return_response_or_raise(e)
end
def parse_body(response)
JSON.parse(response.body, symbolize_names: true)
end
+
+ def return_response_or_raise(error)
+ raise error unless error.respond_to?(:response) && error.response
+
+ error.response
+ end
end
end
end
diff --git a/qa/qa/vendor/github/page/login.rb b/qa/qa/vendor/github/page/login.rb
index f6e72bb01f9..e581edcb7c7 100644
--- a/qa/qa/vendor/github/page/login.rb
+++ b/qa/qa/vendor/github/page/login.rb
@@ -12,11 +12,15 @@ module QA
fill_in 'password', with: QA::Runtime::Env.github_password
click_on 'Sign in'
- otp = OnePassword::CLI.new.otp
+ Support::Retrier.retry_until(exit_on_failure: true, sleep_interval: 35) do
+ otp = OnePassword::CLI.new.otp
- fill_in 'otp', with: otp
+ fill_in 'otp', with: otp
- click_on 'Verify'
+ click_on 'Verify'
+
+ !has_text?('Two-factor authentication failed', wait: 1.0)
+ end
click_on 'Authorize gitlab-qa' if has_button?('Authorize gitlab-qa')
end
diff --git a/qa/qa/vendor/jenkins/page/base.rb b/qa/qa/vendor/jenkins/page/base.rb
new file mode 100644
index 00000000000..8dfbe7570f8
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/base.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class Base
+ include Capybara::DSL
+ include Scenario::Actable
+
+ attr_reader :path
+
+ class << self
+ attr_accessor :host
+ end
+
+ def visit!
+ page.visit URI.join(Base.host, path).to_s
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/jenkins/page/configure.rb b/qa/qa/vendor/jenkins/page/configure.rb
new file mode 100644
index 00000000000..8851a2564fd
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/configure.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class Configure < Page::Base
+ def initialize
+ @path = 'configure'
+ end
+
+ def visit_and_setup_gitlab_connection(gitlab_host, token_description)
+ visit!
+ fill_in '_.name', with: 'GitLab'
+ find('.setting-name', text: "Gitlab host URL").find(:xpath, "..").find('input').set gitlab_host
+
+ dropdown_element = find('.setting-name', text: "Credentials").find(:xpath, "..").find('select')
+
+ QA::Support::Retrier.retry_until(exit_on_failure: true) do
+ dropdown_element.select "GitLab API token (#{token_description})"
+ dropdown_element.value != ''
+ end
+
+ yield if block_given?
+
+ click_save
+ end
+
+ def click_test_connection
+ click_on 'Test Connection'
+ end
+
+ def has_success?
+ has_css?('div.ok', text: "Success")
+ end
+
+ private
+
+ def click_save
+ click_on 'Save'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/jenkins/page/configure_job.rb b/qa/qa/vendor/jenkins/page/configure_job.rb
new file mode 100644
index 00000000000..ab16e895fa9
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/configure_job.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class ConfigureJob < Page::Base
+ attr_accessor :job_name
+
+ def initialize
+ @path = "/job/#{@job_name}/configure"
+ end
+
+ def configure(scm_url:)
+ set_git_source_code_management_url(scm_url)
+ click_build_when_change_is_pushed_to_gitlab
+ set_publish_status_to_gitlab
+ click_save
+ end
+
+ private
+
+ def set_git_source_code_management_url(repository_url)
+ select_git_source_code_management
+ set_repository_url(repository_url)
+ end
+
+ def click_build_when_change_is_pushed_to_gitlab
+ find('label', text: 'Build when a change is pushed to GitLab').find(:xpath, "..").find('input').click
+ end
+
+ def set_publish_status_to_gitlab
+ click_add_post_build_action
+ select_publish_build_status_to_gitlab
+ end
+
+ def click_save
+ click_on 'Save'
+ end
+
+ def select_git_source_code_management
+ find('#radio-block-1').click
+ end
+
+ def set_repository_url(repository_url)
+ find('.setting-name', text: "Repository URL").find(:xpath, "..").find('input').set repository_url
+ end
+
+ def click_add_post_build_action
+ click_on "Add post-build action"
+ end
+
+ def select_publish_build_status_to_gitlab
+ click_link "Publish build status to GitLab"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/jenkins/page/login.rb b/qa/qa/vendor/jenkins/page/login.rb
new file mode 100644
index 00000000000..7b3558b25e2
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/login.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class Login < Page::Base
+ def initialize
+ @path = 'login'
+ end
+
+ def visit!
+ super
+
+ QA::Support::Retrier.retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, exit_on_failure: true) do
+ page.has_text? 'Welcome to Jenkins!'
+ end
+ end
+
+ def login
+ fill_in 'j_username', with: 'admin'
+ fill_in 'j_password', with: 'password'
+ click_on 'Sign in'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/jenkins/page/new_credentials.rb b/qa/qa/vendor/jenkins/page/new_credentials.rb
new file mode 100644
index 00000000000..bdef1a13fd4
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/new_credentials.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class NewCredentials < Page::Base
+ def initialize
+ @path = 'credentials/store/system/domain/_/newCredentials'
+ end
+
+ def visit_and_set_gitlab_api_token(api_token, description)
+ visit!
+ wait_for_page_to_load
+ select_gitlab_api_token
+ set_api_token(api_token)
+ set_description(description)
+ click_ok
+ end
+
+ private
+
+ def select_gitlab_api_token
+ find('.setting-name', text: "Kind").find(:xpath, "..").find('select').select "GitLab API token"
+ end
+
+ def set_api_token(api_token)
+ fill_in '_.apiToken', with: api_token
+ end
+
+ def set_description(description)
+ fill_in '_.description', with: description
+ end
+
+ def click_ok
+ click_on 'OK'
+ end
+
+ def wait_for_page_to_load
+ QA::Support::Waiter.wait(interval: 1.0) do
+ page.has_css?('.setting-name', text: "Description")
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/jenkins/page/new_job.rb b/qa/qa/vendor/jenkins/page/new_job.rb
new file mode 100644
index 00000000000..11fa4ca8a53
--- /dev/null
+++ b/qa/qa/vendor/jenkins/page/new_job.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'capybara/dsl'
+
+module QA
+ module Vendor
+ module Jenkins
+ module Page
+ class NewJob < Page::Base
+ def initialize
+ @path = 'newJob'
+ end
+
+ def visit_and_create_new_job_with_name(new_job_name)
+ visit!
+ set_new_job_name(new_job_name)
+ click_free_style_project
+ click_ok
+ end
+
+ private
+
+ def set_new_job_name(new_job_name)
+ fill_in 'name', with: new_job_name
+ end
+
+ def click_free_style_project
+ find('.hudson_model_FreeStyleProject').click
+ end
+
+ def click_ok
+ click_on 'OK'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/vendor/saml_idp/page/login.rb b/qa/qa/vendor/saml_idp/page/login.rb
index 1b8c926532a..9ebcabe15fc 100644
--- a/qa/qa/vendor/saml_idp/page/login.rb
+++ b/qa/qa/vendor/saml_idp/page/login.rb
@@ -7,18 +7,22 @@ module QA
module SAMLIdp
module Page
class Login < Page::Base
- def login
- fill_in 'username', with: 'user1'
- fill_in 'password', with: 'user1pass'
+ def login(username, password)
+ QA::Runtime::Logger.debug("Logging into SAMLIdp with username: #{username} and password:#{password}") if QA::Runtime::Env.debug?
+
+ fill_in 'username', with: username
+ fill_in 'password', with: password
click_on 'Login'
end
- def login_if_required
- login if login_required?
+ def login_if_required(username, password)
+ login(username, password) if login_required?
end
def login_required?
- page.has_text?('Enter your username and password')
+ login_required = page.has_text?('Enter your username and password')
+ QA::Runtime::Logger.debug("login_required: #{login_required}") if QA::Runtime::Env.debug?
+ login_required
end
end
end