Commit 4ca162db authored by Sanad Liaquat's avatar Sanad Liaquat

Merge branch 'dirty-fix-for-prepended-modules' into 'master'

Fix including/prepending QA modules

Closes #213208

See merge request gitlab-org/gitlab!28694
parents 95ccd4d2 4d36096c
...@@ -238,6 +238,53 @@ the view for code in a library. ...@@ -238,6 +238,53 @@ the view for code in a library.
In such rare cases it's reasonable to use CSS selectors in page object methods, In such rare cases it's reasonable to use CSS selectors in page object methods,
with a comment explaining why an `element` can't be added. with a comment explaining why an `element` can't be added.
### Define Page concerns
Some pages share common behaviors, and/or are prepended with EE-specific modules that adds EE-specific methods.
These modules must:
1. Extend from the `QA::Page::PageConcern` module, with `extend QA::Page::PageConcern`.
1. Override the `self.prepended` method if they need to `include`/`prepend` other modules themselves, and/or define
`view` or `elements`.
1. Call `super` as the first thing in `self.prepended`.
1. Include/prepend other modules and define their `view`/`elements` in a `base.class_eval` block to ensure they're
defined in the class that prepends the module.
These steps ensure the sanity selectors check will detect problems properly.
For example, `qa/qa/ee/page/merge_request/show.rb` adds EE-specific methods to `qa/qa/page/merge_request/show.rb` (with
`QA::Page::MergeRequest::Show.prepend_if_ee('QA::EE::Page::MergeRequest::Show')`) and following is how it's implemented
(only showing the relevant part and refering to the 4 steps described above with inline comments):
```ruby
module QA
module EE
module Page
module MergeRequest
module Show
extend QA::Page::PageConcern # 1.
def self.prepended(base) # 2.
super # 3.
base.class_eval do # 4.
prepend Page::Component::LicenseManagement
view 'app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue' do
element :head_mismatch, "The source branch HEAD has recently changed."
end
[...]
end
end
end
end
end
end
end
```
## Running the test locally ## Running the test locally
During development, you can run the `qa:selectors` test by running During development, you can run the `qa:selectors` test by running
......
...@@ -162,6 +162,7 @@ module QA ...@@ -162,6 +162,7 @@ module QA
autoload :Base, 'qa/page/base' autoload :Base, 'qa/page/base'
autoload :View, 'qa/page/view' autoload :View, 'qa/page/view'
autoload :Element, 'qa/page/element' autoload :Element, 'qa/page/element'
autoload :PageConcern, 'qa/page/page_concern'
autoload :Validator, 'qa/page/validator' autoload :Validator, 'qa/page/validator'
autoload :Validatable, 'qa/page/validatable' autoload :Validatable, 'qa/page/validatable'
...@@ -248,7 +249,6 @@ module QA ...@@ -248,7 +249,6 @@ module QA
end end
module Settings module Settings
autoload :Common, 'qa/page/project/settings/common'
autoload :Advanced, 'qa/page/project/settings/advanced' autoload :Advanced, 'qa/page/project/settings/advanced'
autoload :Main, 'qa/page/project/settings/main' autoload :Main, 'qa/page/project/settings/main'
autoload :Repository, 'qa/page/project/settings/repository' autoload :Repository, 'qa/page/project/settings/repository'
...@@ -409,6 +409,7 @@ module QA ...@@ -409,6 +409,7 @@ module QA
autoload :Breadcrumbs, 'qa/page/component/breadcrumbs' autoload :Breadcrumbs, 'qa/page/component/breadcrumbs'
autoload :CiBadgeLink, 'qa/page/component/ci_badge_link' autoload :CiBadgeLink, 'qa/page/component/ci_badge_link'
autoload :ClonePanel, 'qa/page/component/clone_panel' autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :DesignManagement, 'qa/page/component/design_management'
autoload :LazyLoader, 'qa/page/component/lazy_loader' autoload :LazyLoader, 'qa/page/component/lazy_loader'
autoload :LegacyClonePanel, 'qa/page/component/legacy_clone_panel' autoload :LegacyClonePanel, 'qa/page/component/legacy_clone_panel'
autoload :Dropzone, 'qa/page/component/dropzone' autoload :Dropzone, 'qa/page/component/dropzone'
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module Admin module Admin
module Menu module Menu
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/views/layouts/nav/sidebar/_admin.html.haml' do view 'app/views/layouts/nav/sidebar/_admin.html.haml' do
element :admin_settings_template_item element :admin_settings_template_item
end end
......
...@@ -7,8 +7,12 @@ module QA ...@@ -7,8 +7,12 @@ module QA
module Overview module Overview
module Groups module Groups
module Edit module Edit
def self.included(page) extend QA::Page::PageConcern
page.class_eval do
def self.included(base)
super
base.class_eval do
view 'ee/app/views/admin/_namespace_plan.html.haml' do view 'ee/app/views/admin/_namespace_plan.html.haml' do
element :plan_dropdown element :plan_dropdown
end end
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module Component module Component
module LicenseManagement module LicenseManagement
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/assets/javascripts/reports/components/report_item.vue' do view 'app/assets/javascripts/reports/components/report_item.vue' do
element :report_item_row element :report_item_row
end end
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module Component module Component
module SecureReport module SecureReport
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/assets/javascripts/security_dashboard/components/filter.vue' do view 'ee/app/assets/javascripts/security_dashboard/components/filter.vue' do
element :filter_dropdown, ':data-qa-selector="qaSelector"' # rubocop:disable QA/ElementWithPattern element :filter_dropdown, ':data-qa-selector="qaSelector"' # rubocop:disable QA/ElementWithPattern
element :filter_dropdown_content element :filter_dropdown_content
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Component module Component
module WebIDE module WebIDE
module WebTerminalPanel module WebTerminalPanel
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue' do view 'app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue' do
element :ide_right_sidebar, %q(:data-qa-selector="`ide_${side}_sidebar`") # rubocop:disable QA/ElementWithPattern element :ide_right_sidebar, %q(:data-qa-selector="`ide_${side}_sidebar`") # rubocop:disable QA/ElementWithPattern
element :terminal_tab_button, %q(:data-qa-selector="`${tab.title.toLowerCase()}_tab_button`") # rubocop:disable QA/ElementWithPattern element :terminal_tab_button, %q(:data-qa-selector="`${tab.title.toLowerCase()}_tab_button`") # rubocop:disable QA/ElementWithPattern
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module Dashboard module Dashboard
module Projects module Projects
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/views/shared/projects/_list.html.haml' do view 'app/views/shared/projects/_list.html.haml' do
element :projects_list element :projects_list
end end
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module File module File
module Show module Show
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/blob/_owners.html.haml' do view 'ee/app/views/projects/blob/_owners.html.haml' do
element :file_owner_content element :file_owner_content
element :link_file_owner element :link_file_owner
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Page module Page
module Group module Group
module Menu module Menu
prepend QA::Page::Group::SubMenus::Common extend QA::Page::PageConcern
def self.prepended(base) def self.prepended(base)
super
base.class_eval do base.class_eval do
prepend QA::Page::Group::SubMenus::Common
view 'ee/app/views/groups/ee/_settings_nav.html.haml' do view 'ee/app/views/groups/ee/_settings_nav.html.haml' do
element :ldap_synchronization_link element :ldap_synchronization_link
element :audit_events_settings_link element :audit_events_settings_link
......
...@@ -3,18 +3,20 @@ ...@@ -3,18 +3,20 @@
module QA module QA
module EE module EE
module Page module Page
module Group::Secure module Group
class Show < QA::Page::Base module Secure
include Page::Component::SecureReport class Show < QA::Page::Base
include Page::Component::SecureReport
view 'ee/app/assets/javascripts/security_dashboard/components/security_dashboard_table.vue' do view 'ee/app/assets/javascripts/security_dashboard/components/security_dashboard_table.vue' do
element :security_report_content, required: true element :security_report_content, required: true
end end
def filter_project(project) def filter_project(project)
click_element(:filter_project_dropdown) click_element(:filter_project_dropdown)
within_element(:filter_dropdown_content) do within_element(:filter_dropdown_content) do
click_on project click_on project
end
end end
end end
end end
......
...@@ -6,11 +6,15 @@ module QA ...@@ -6,11 +6,15 @@ module QA
module Group module Group
module Settings module Settings
module General module General
prepend ::QA::Page::Component::Select2 extend QA::Page::PageConcern
prepend ::QA::Page::Settings::Common
def self.prepended(base) def self.prepended(base)
super
base.class_eval do base.class_eval do
prepend ::QA::Page::Component::Select2
prepend ::QA::Page::Settings::Common
view 'ee/app/views/groups/_custom_project_templates_setting.html.haml' do view 'ee/app/views/groups/_custom_project_templates_setting.html.haml' do
element :custom_project_template_select element :custom_project_template_select
element :custom_project_templates element :custom_project_templates
......
...@@ -8,6 +8,7 @@ module QA ...@@ -8,6 +8,7 @@ module QA
view 'ee/app/assets/javascripts/insights/components/insights.vue' do view 'ee/app/assets/javascripts/insights/components/insights.vue' do
element :insights_dashboard_dropdown element :insights_dashboard_dropdown
end end
view 'ee/app/assets/javascripts/insights/components/insights_page.vue' do view 'ee/app/assets/javascripts/insights/components/insights_page.vue' do
element :insights_charts element :insights_charts
element :insights_page element :insights_page
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Page module Page
module MergeRequest module MergeRequest
module New module New
include QA::Page::Component::Select2 extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
prepend ::QA::Page::Component::Select2
def self.prepended(page)
page.module_eval do
view 'ee/app/assets/javascripts/approvals/components/app.vue' do view 'ee/app/assets/javascripts/approvals/components/app.vue' do
element :add_approvers_button element :add_approvers_button
end end
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Page module Page
module MergeRequest module MergeRequest
module Show module Show
include Page::Component::LicenseManagement extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
prepend Page::Component::LicenseManagement
def self.prepended(page)
page.module_eval do
view 'app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue' do view 'app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue' do
element :head_mismatch, "The source branch HEAD has recently changed." # rubocop:disable QA/ElementWithPattern element :head_mismatch, "The source branch HEAD has recently changed." # rubocop:disable QA/ElementWithPattern
end end
......
...@@ -5,6 +5,8 @@ module QA ...@@ -5,6 +5,8 @@ module QA
module Page module Page
module Profile module Profile
module Menu module Menu
extend QA::Page::PageConcern
def wait_for_key_to_replicate(text, max_wait: Runtime::Geo.max_file_replication_time) def wait_for_key_to_replicate(text, max_wait: Runtime::Geo.max_file_replication_time)
wait_until(max_duration: max_wait) { page.has_text?(text) } wait_until(max_duration: max_wait) { page.has_text?(text) }
end end
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Project module Project
module Issue module Issue
module Index module Index
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/views/shared/issuable/_search_bar.html.haml' do view 'app/views/shared/issuable/_search_bar.html.haml' do
element :issue_filter_form, /form_tag.+class: 'filter-form / # rubocop:disable QA/ElementWithPattern element :issue_filter_form, /form_tag.+class: 'filter-form / # rubocop:disable QA/ElementWithPattern
element :issue_filter_input, /%input.form-control.filtered-search/ # rubocop:disable QA/ElementWithPattern element :issue_filter_input, /%input.form-control.filtered-search/ # rubocop:disable QA/ElementWithPattern
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Project module Project
module Issue module Issue
module Show module Show
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/assets/javascripts/related_issues/components/add_issuable_form.vue' do view 'ee/app/assets/javascripts/related_issues/components/add_issuable_form.vue' do
element :add_issue_button element :add_issue_button
end end
......
...@@ -5,11 +5,20 @@ module QA ...@@ -5,11 +5,20 @@ module QA
module Page module Page
module Project module Project
module Menu module Menu
prepend QA::Page::Project::SubMenus::Common extend QA::Page::PageConcern
prepend SubMenus::SecurityCompliance
prepend SubMenus::Packages def self.prepended(base)
prepend SubMenus::Project super
prepend SubMenus::Settings
base.class_eval do
prepend QA::Page::Project::SubMenus::Common
prepend SubMenus::SecurityCompliance
prepend SubMenus::Packages
prepend SubMenus::Project
prepend SubMenus::Repository
prepend SubMenus::Settings
end
end
end end
end end
end end
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Page module Page
module Project module Project
module New module New
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/_project_templates.html.haml' do view 'ee/app/views/projects/_project_templates.html.haml' do
element :group_templates_tab element :group_templates_tab
element :group_template_tab_badge element :group_template_tab_badge
......
...@@ -7,8 +7,12 @@ module QA ...@@ -7,8 +7,12 @@ module QA
module Operations module Operations
module Kubernetes module Kubernetes
module Show module Show
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/clusters/clusters/_health.html.haml' do view 'ee/app/views/clusters/clusters/_health.html.haml' do
element :cluster_health_section element :cluster_health_section
end end
......
...@@ -7,10 +7,14 @@ module QA ...@@ -7,10 +7,14 @@ module QA
module Operations module Operations
module Metrics module Metrics
module Show module Show
extend QA::Page::PageConcern
EXPECTED_LABEL = 'Total (GB)' EXPECTED_LABEL = 'Total (GB)'
def self.prepended(page) def self.prepended(base)
page.module_eval do super
base.class_eval do
view 'app/assets/javascripts/monitoring/components/alert_widget_form.vue' do view 'app/assets/javascripts/monitoring/components/alert_widget_form.vue' do
element :alert_query_dropdown element :alert_query_dropdown
element :alert_query_option element :alert_query_option
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::EE module QA
module Page::Project module EE
module Pipeline module Page
module Show module Project
include Page::Component::LicenseManagement module Pipeline
include Page::Component::SecureReport module Show
extend QA::Page::PageConcern
def self.prepended(page) def self.prepended(base)
page.module_eval do super
view 'ee/app/views/projects/pipelines/_tabs_holder.html.haml' do
element :security_tab base.class_eval do
element :licenses_tab include Page::Component::LicenseManagement
element :licenses_counter include Page::Component::SecureReport
view 'ee/app/views/projects/pipelines/_tabs_holder.html.haml' do
element :security_tab
element :licenses_tab
element :licenses_counter
end
end
end end
end
end
def click_on_security def click_on_security
click_element(:security_tab) click_element(:security_tab)
end end
def click_on_licenses def click_on_licenses
click_element(:licenses_tab) click_element(:licenses_tab)
end end
def has_license_count_of?(count) def has_license_count_of?(count)
find_element(:licenses_counter).has_content?(count) find_element(:licenses_counter).has_content?(count)
end
end
end end
end end
end end
......
...@@ -3,14 +3,16 @@ ...@@ -3,14 +3,16 @@
module QA module QA
module EE module EE
module Page module Page
module Project::Secure module Project
class DependencyList < QA::Page::Base module Secure
view 'ee/app/assets/javascripts/dependencies/components/app.vue' do class DependencyList < QA::Page::Base
element :dependency_list_all_count, "dependency_list_${label.toLowerCase().replace(' ', '_')" # rubocop:disable QA/ElementWithPattern view 'ee/app/assets/javascripts/dependencies/components/app.vue' do
end element :dependency_list_all_count, "dependency_list_${label.toLowerCase().replace(' ', '_')" # rubocop:disable QA/ElementWithPattern
end
def has_dependency_count_of?(expected) def has_dependency_count_of?(expected)
find_element(:dependency_list_all_count).has_content?(expected) find_element(:dependency_list_all_count).has_content?(expected)
end
end end
end end
end end
......
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
module QA module QA
module EE module EE
module Page module Page
module Project::Secure module Project
class Show < QA::Page::Base module Secure
include Page::Component::SecureReport class Show < QA::Page::Base
include Page::Component::SecureReport
view 'ee/app/assets/javascripts/security_dashboard/components/security_dashboard_table.vue' do view 'ee/app/assets/javascripts/security_dashboard/components/security_dashboard_table.vue' do
element :security_report_content, required: true element :security_report_content, required: true
end
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::EE module QA
module Page module EE
module Project module Page
module Settings module Project
module CICD module Settings
def self.prepended(page) module CICD
page.module_eval do extend QA::Page::PageConcern
view 'ee/app/views/projects/settings/ci_cd/_managed_licenses.html.haml' do
element :license_compliance_settings_content def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/settings/ci_cd/_managed_licenses.html.haml' do
element :license_compliance_settings_content
end
end end
end end
end
def expand_license_compliance(&block) def expand_license_compliance(&block)
expand_section(:license_compliance_settings_content) do expand_section(:license_compliance_settings_content) do
Settings::LicenseCompliance.perform(&block) Settings::LicenseCompliance.perform(&block)
end
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::EE module QA
module Page module EE
module Project module Page
module Settings module Project
class LicenseCompliance < QA::Page::Base module Settings
include QA::Page::Component::Select2 # Select2 is an external library, so we can't add our own selectors class LicenseCompliance < QA::Page::Base
include QA::Page::Component::Select2 # Select2 is an external library, so we can't add our own selectors
view 'ee/app/assets/javascripts/vue_shared/license_compliance/license_management.vue' do view 'ee/app/assets/javascripts/vue_shared/license_compliance/license_management.vue' do
element :license_add_button element :license_add_button
end end
view 'ee/app/assets/javascripts/vue_shared/license_compliance/components/add_license_form.vue' do view 'ee/app/assets/javascripts/vue_shared/license_compliance/components/add_license_form.vue' do
element :license_radio, 'data-qa-selector="`${option.value}_license_radio`"' # rubocop:disable QA/ElementWithPattern element :license_radio, 'data-qa-selector="`${option.value}_license_radio`"' # rubocop:disable QA/ElementWithPattern
element :add_license_submit_button element :add_license_submit_button
end end
view 'ee/app/assets/javascripts/vue_shared/license_compliance/license_management.vue' do view 'ee/app/assets/javascripts/vue_shared/license_compliance/license_management.vue' do
element :license_compliance_list element :license_compliance_list
end end
view 'ee/app/assets/javascripts/vue_shared/license_compliance/components/admin_license_management_row.vue' do view 'ee/app/assets/javascripts/vue_shared/license_compliance/components/admin_license_management_row.vue' do
element :admin_license_compliance_row element :admin_license_compliance_row
element :license_name_content element :license_name_content
end end
view 'app/assets/javascripts/reports/components/issue_status_icon.vue' do view 'app/assets/javascripts/reports/components/issue_status_icon.vue' do
element :icon_status, ':data-qa-selector="`status_${status}_icon`" ' # rubocop:disable QA/ElementWithPattern element :icon_status, ':data-qa-selector="`status_${status}_icon`" ' # rubocop:disable QA/ElementWithPattern
end end
def has_approved_license?(name) def has_approved_license?(name)
within_element(:admin_license_compliance_row, text: name) do within_element(:admin_license_compliance_row, text: name) do
has_element?(:status_success_icon) has_element?(:status_success_icon)
end
end end
end
def has_denied_license?(name) def has_denied_license?(name)
within_element(:admin_license_compliance_row, text: name) do within_element(:admin_license_compliance_row, text: name) do
has_element?(:status_failed_icon) has_element?(:status_failed_icon)
end
end end
end
def approve_license(license) def approve_license(license)
click_element :license_add_button click_element :license_add_button
expand_select_list expand_select_list
search_and_select license search_and_select license
click_element :approved_license_radio click_element :approved_license_radio
click_element :add_license_submit_button click_element :add_license_submit_button
has_approved_license? license has_approved_license? license
end end
def deny_license(license) def deny_license(license)
click_element :license_add_button click_element :license_add_button
expand_select_list expand_select_list
search_and_select license search_and_select license
click_element :blacklisted_license_radio click_element :blacklisted_license_radio
click_element :add_license_submit_button click_element :add_license_submit_button
has_denied_license? license has_denied_license? license
end
end end
end end
end end
......
...@@ -6,10 +6,14 @@ module QA ...@@ -6,10 +6,14 @@ module QA
module Project module Project
module Settings module Settings
module MergeRequest module MergeRequest
include Page::Component::SecureReport extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
include Page::Component::SecureReport
def self.prepended(page)
page.module_eval do
view 'ee/app/views/projects/_merge_pipelines_settings.html.haml' do view 'ee/app/views/projects/_merge_pipelines_settings.html.haml' do
element :merged_results_pipeline_checkbox element :merged_results_pipeline_checkbox
end end
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Project module Project
module Settings module Settings
module MirroringRepositories module MirroringRepositories
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/mirrors/_mirror_repos_form.html.haml' do view 'ee/app/views/projects/mirrors/_mirror_repos_form.html.haml' do
element :mirror_direction element :mirror_direction
end end
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Project module Project
module Settings module Settings
module ProtectedBranches module ProtectedBranches
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml' do view 'ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml' do
element :allowed_to_push_select element :allowed_to_push_select
element :allowed_to_push_dropdown element :allowed_to_push_dropdown
......
...@@ -6,8 +6,12 @@ module QA ...@@ -6,8 +6,12 @@ module QA
module Project module Project
module Settings module Settings
module Repository module Repository
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'ee/app/views/projects/push_rules/_index.html.haml' do view 'ee/app/views/projects/push_rules/_index.html.haml' do
element :push_rules_content element :push_rules_content
end end
......
...@@ -5,6 +5,8 @@ module QA ...@@ -5,6 +5,8 @@ module QA
module Page module Page
module Project module Project
module Show module Show
extend QA::Page::PageConcern
def wait_for_repository_replication(max_wait: Runtime::Geo.max_file_replication_time) def wait_for_repository_replication(max_wait: Runtime::Geo.max_file_replication_time)
QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_repository_replication]) QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_repository_replication])
wait_until_geo_max_replication_time(max_wait: max_wait) do wait_until_geo_max_replication_time(max_wait: max_wait) do
......
...@@ -6,8 +6,10 @@ module QA ...@@ -6,8 +6,10 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Packages module Packages
def self.included(page) extend QA::Page::PageConcern
page.class_eval do
def self.prepended(base)
base.class_eval do
view 'ee/app/views/layouts/nav/sidebar/_project_packages_link.html.haml' do view 'ee/app/views/layouts/nav/sidebar/_project_packages_link.html.haml' do
element :packages_link element :packages_link
end end
......
...@@ -6,8 +6,10 @@ module QA ...@@ -6,8 +6,10 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Repository module Repository
def self.included(page) extend QA::Page::PageConcern
page.class_eval do
def self.prepended(base)
base.class_eval do
view 'ee/app/views/projects/sidebar/_repository_locked_files.html.haml' do view 'ee/app/views/projects/sidebar/_repository_locked_files.html.haml' do
element :path_locks_link element :path_locks_link
end end
......
...@@ -6,8 +6,10 @@ module QA ...@@ -6,8 +6,10 @@ module QA
module Project module Project
module SubMenus module SubMenus
module SecurityCompliance module SecurityCompliance
def self.included(page) extend QA::Page::PageConcern
page.class_eval do
def self.prepended(base)
base.class_eval do
view 'ee/app/views/layouts/nav/sidebar/_project_security_link.html.haml' do view 'ee/app/views/layouts/nav/sidebar/_project_security_link.html.haml' do
element :security_dashboard_link element :security_dashboard_link
element :dependency_list_link element :dependency_list_link
......
...@@ -6,10 +6,12 @@ module QA ...@@ -6,10 +6,12 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Settings module Settings
include QA::Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.prepended(base)
base.class_eval do base.class_eval do
prepend QA::Page::Project::SubMenus::Common
view 'ee/app/views/projects/sidebar/_settings_audit_events.html.haml' do view 'ee/app/views/projects/sidebar/_settings_audit_events.html.haml' do
element :audit_events_settings_link element :audit_events_settings_link
end end
......
...@@ -6,6 +6,8 @@ module QA ...@@ -6,6 +6,8 @@ module QA
module Project module Project
module Wiki module Wiki
module Show module Show
extend QA::Page::PageConcern
def wait_for_repository_replication(max_wait: Runtime::Geo.max_file_replication_time) def wait_for_repository_replication(max_wait: Runtime::Geo.max_file_replication_time)
QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_repository_replication]) QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_repository_replication])
wait_until_geo_max_replication_time(max_wait: max_wait) do wait_until_geo_max_replication_time(max_wait: max_wait) do
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module Breadcrumbs module Breadcrumbs
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/layouts/nav/_breadcrumbs.html.haml' do base.view 'app/views/layouts/nav/_breadcrumbs.html.haml' do
element :breadcrumb_links_content element :breadcrumb_links_content
end end
......
...@@ -4,6 +4,8 @@ module QA ...@@ -4,6 +4,8 @@ module QA
module Page module Page
module Component module Component
module CiBadgeLink module CiBadgeLink
extend QA::Page::PageConcern
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
INCOMPLETE_STATUSES = %w[pending created running].freeze INCOMPLETE_STATUSES = %w[pending created running].freeze
...@@ -27,6 +29,8 @@ module QA ...@@ -27,6 +29,8 @@ module QA
end end
def self.included(base) def self.included(base)
super
base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
element :status_badge element :status_badge
end end
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module ClonePanel module ClonePanel
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/projects/buttons/_clone.html.haml' do base.view 'app/views/projects/buttons/_clone.html.haml' do
element :clone_dropdown element :clone_dropdown
element :clone_options element :clone_options
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module ConfirmModal module ConfirmModal
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/shared/_confirm_modal.html.haml' do base.view 'app/views/shared/_confirm_modal.html.haml' do
element :confirm_modal element :confirm_modal
element :confirm_input element :confirm_input
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module CustomMetric module CustomMetric
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue' do base.view 'app/assets/javascripts/custom_metrics/components/custom_metrics_form_fields.vue' do
element :custom_metric_prometheus_title_field element :custom_metric_prometheus_title_field
element :custom_metric_prometheus_query_field element :custom_metric_prometheus_query_field
......
...@@ -4,8 +4,12 @@ module QA ...@@ -4,8 +4,12 @@ module QA
module Page module Page
module Component module Component
module DesignManagement module DesignManagement
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.included(base)
super
base.class_eval do
view 'app/assets/javascripts/design_management/components/design_notes/design_discussion.vue' do view 'app/assets/javascripts/design_management/components/design_notes/design_discussion.vue' do
element :design_discussion_content element :design_discussion_content
end end
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module GroupsFilter module GroupsFilter
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/shared/groups/_search_form.html.haml' do base.view 'app/views/shared/groups/_search_form.html.haml' do
element :groups_filter element :groups_filter
end end
......
...@@ -5,7 +5,11 @@ module QA ...@@ -5,7 +5,11 @@ module QA
module Component module Component
module Issuable module Issuable
module Common module Common
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/assets/javascripts/issue_show/components/title.vue' do base.view 'app/assets/javascripts/issue_show/components/title.vue' do
element :edit_button element :edit_button
element :title, required: true element :title, required: true
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module LazyLoader module LazyLoader
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/assets/javascripts/lazy_loader.js' do base.view 'app/assets/javascripts/lazy_loader.js' do
element :js_lazy_loaded element :js_lazy_loaded
end end
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module LegacyClonePanel module LegacyClonePanel
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/shared/_clone_panel.html.haml' do base.view 'app/views/shared/_clone_panel.html.haml' do
element :clone_dropdown element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern element :clone_options_dropdown, '.clone-options-dropdown' # rubocop:disable QA/ElementWithPattern
......
...@@ -4,7 +4,11 @@ module QA ...@@ -4,7 +4,11 @@ module QA
module Page module Page
module Component module Component
module Note module Note
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/assets/javascripts/notes/components/comment_form.vue' do base.view 'app/assets/javascripts/notes/components/comment_form.vue' do
element :note_dropdown element :note_dropdown
element :discussion_option element :discussion_option
......
...@@ -5,8 +5,12 @@ module QA ...@@ -5,8 +5,12 @@ module QA
module Component module Component
module WebIDE module WebIDE
module Alert module Alert
def self.prepended(page) extend QA::Page::PageConcern
page.module_eval do
def self.prepended(base)
super
base.class_eval do
view 'app/assets/javascripts/ide/components/error_message.vue' do view 'app/assets/javascripts/ide/components/error_message.vue' do
element :flash_alert element :flash_alert
end end
......
...@@ -5,7 +5,11 @@ module QA ...@@ -5,7 +5,11 @@ module QA
module File module File
module Shared module Shared
module CommitButton module CommitButton
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/projects/_commit_button.html.haml' do base.view 'app/views/projects/_commit_button.html.haml' do
element :commit_button element :commit_button
end end
......
...@@ -5,7 +5,11 @@ module QA ...@@ -5,7 +5,11 @@ module QA
module File module File
module Shared module Shared
module CommitMessage module CommitMessage
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/shared/_commit_message_container.html.haml' do base.view 'app/views/shared/_commit_message_container.html.haml' do
element :commit_message, "text_area_tag 'commit_message'" # rubocop:disable QA/ElementWithPattern element :commit_message, "text_area_tag 'commit_message'" # rubocop:disable QA/ElementWithPattern
end end
......
...@@ -5,7 +5,11 @@ module QA ...@@ -5,7 +5,11 @@ module QA
module File module File
module Shared module Shared
module Editor module Editor
extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.view 'app/views/projects/blob/_editor.html.haml' do base.view 'app/views/projects/blob/_editor.html.haml' do
element :editor element :editor
end end
......
...@@ -5,9 +5,12 @@ module QA ...@@ -5,9 +5,12 @@ module QA
module Group module Group
module SubMenus module SubMenus
module Common module Common
extend QA::Page::PageConcern
include QA::Page::SubMenus::Common include QA::Page::SubMenus::Common
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
view 'app/views/layouts/nav/sidebar/_group.html.haml' do view 'app/views/layouts/nav/sidebar/_group.html.haml' do
element :group_sidebar element :group_sidebar
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
module Page::Main module Page
class Terms < Page::Base module Main
view 'app/views/layouts/terms.html.haml' do class Terms < Page::Base
element :user_avatar, required: true view 'app/views/layouts/terms.html.haml' do
end element :user_avatar, required: true
end
view 'app/views/users/terms/index.html.haml' do view 'app/views/users/terms/index.html.haml' do
element :terms_content, required: true element :terms_content, required: true
element :accept_terms_button element :accept_terms_button
end end
def accept_terms def accept_terms
click_element :accept_terms_button, Page::Main::Menu click_element :accept_terms_button, Page::Main::Menu
end
end end
end end
end end
......
module QA
module Page
module PageConcern
def included(base)
unless base.is_a?(Class)
raise "Expected #{self} to be prepended to a class, but #{base} is a module!"
end
unless base.ancestors.include?(::QA::Page::Base)
raise "Expected #{self} to be prepended to a class that inherits from ::QA::Page::Base, but #{base} doesn't!"
end
end
alias_method :prepended, :included
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module QA::Page module QA
module Project::Job module Page
class Show < QA::Page::Base module Project
include Component::CiBadgeLink module Job
class Show < QA::Page::Base
include Component::CiBadgeLink
view 'app/assets/javascripts/jobs/components/log/log.vue' do view 'app/assets/javascripts/jobs/components/log/log.vue' do
element :job_log_content element :job_log_content
end end
view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do
element :pipeline_path element :pipeline_path
end end
view 'app/assets/javascripts/jobs/components/sidebar.vue' do view 'app/assets/javascripts/jobs/components/sidebar.vue' do
element :retry_button element :retry_button
end end
def successful?(timeout: 60) def successful?(timeout: 60)
raise "Timed out waiting for the build trace to load" unless loaded? raise "Timed out waiting for the build trace to load" unless loaded?
raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout) raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
passed? passed?
end end
# Reminder: You may wish to wait for a particular job status before checking output # Reminder: You may wish to wait for a particular job status before checking output
def output(wait: 5) def output(wait: 5)
result = '' result = ''
wait_until(reload: false, max_duration: wait, sleep_interval: 1) do wait_until(reload: false, max_duration: wait, sleep_interval: 1) do
result = find_element(:job_log_content).text result = find_element(:job_log_content).text
result.include?('Job') result.include?('Job')
end end
result result
end end
def retry! def retry!
click_element :retry_button click_element :retry_button
end end
private private
def loaded?(wait: 60) def loaded?(wait: 60)
wait_until(reload: true, max_duration: wait, sleep_interval: 1) do wait_until(reload: true, max_duration: wait, sleep_interval: 1) do
has_element?(:job_log_content, wait: 1) has_element?(:job_log_content, wait: 1)
end
end
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::Page module QA
module Project::Pipeline module Page
class Index < QA::Page::Base module Project
view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do module Pipeline
element :pipeline_url_link class Index < QA::Page::Base
end view 'app/assets/javascripts/pipelines/components/pipeline_url.vue' do
element :pipeline_url_link
view 'app/assets/javascripts/pipelines/components/pipelines_table_row.vue' do end
element :pipeline_commit_status
element :pipeline_retry_button view 'app/assets/javascripts/pipelines/components/pipelines_table_row.vue' do
end element :pipeline_commit_status
element :pipeline_retry_button
def click_on_latest_pipeline end
all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click
end def click_on_latest_pipeline
all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click
def wait_for_latest_pipeline_success end
wait_for_latest_pipeline_status { has_text?('passed') }
end def wait_for_latest_pipeline_success
wait_for_latest_pipeline_status { has_text?('passed') }
def wait_for_latest_pipeline_completion end
wait_for_latest_pipeline_status { has_text?('passed') || has_text?('failed') }
end def wait_for_latest_pipeline_completion
wait_for_latest_pipeline_status { has_text?('passed') || has_text?('failed') }
def wait_for_latest_pipeline_status end
wait_until(reload: false, max_duration: 360) do
within_element_by_index(:pipeline_commit_status, 0) { yield } def wait_for_latest_pipeline_status
end wait_until(reload: false, max_duration: 360) do
end within_element_by_index(:pipeline_commit_status, 0) { yield }
end
def wait_for_latest_pipeline_success_or_retry end
wait_for_latest_pipeline_completion
def wait_for_latest_pipeline_success_or_retry
if has_text?('failed') wait_for_latest_pipeline_completion
click_element :pipeline_retry_button
wait_for_latest_pipeline_success if has_text?('failed')
click_element :pipeline_retry_button
wait_for_latest_pipeline_success
end
end
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::Page module QA
module Project::Pipeline module Page
class Show < QA::Page::Base module Project
include Component::CiBadgeLink module Pipeline
class Show < QA::Page::Base
view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do include Component::CiBadgeLink
element :pipeline_header, /header class.*ci-header-container.*/ # rubocop:disable QA/ElementWithPattern
end view 'app/assets/javascripts/vue_shared/components/header_ci_component.vue' do
element :pipeline_header, /header class.*ci-header-container.*/ # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do view 'app/assets/javascripts/pipelines/components/graph/graph_component.vue' do
element :pipeline_graph, /class.*pipeline-graph.*/ # rubocop:disable QA/ElementWithPattern element :pipeline_graph, /class.*pipeline-graph.*/ # rubocop:disable QA/ElementWithPattern
end end
view 'app/assets/javascripts/pipelines/components/graph/job_item.vue' do view 'app/assets/javascripts/pipelines/components/graph/job_item.vue' do
element :job_component, /class.*ci-job-component.*/ # rubocop:disable QA/ElementWithPattern element :job_component, /class.*ci-job-component.*/ # rubocop:disable QA/ElementWithPattern
element :job_link element :job_link
end end
view 'app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue' do view 'app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue' do
element :linked_pipeline_button element :linked_pipeline_button
end end
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end end
view 'app/views/projects/pipelines/_info.html.haml' do view 'app/views/projects/pipelines/_info.html.haml' do
element :pipeline_badges element :pipeline_badges
end end
def running?(wait: 0) def running?(wait: 0)
within('.ci-header-container') do within('.ci-header-container') do
page.has_content?('running', wait: wait) page.has_content?('running', wait: wait)
end end
end end
def has_build?(name, status: :success, wait: nil) def has_build?(name, status: :success, wait: nil)
within('.pipeline-graph') do within('.pipeline-graph') do
within('.ci-job-component', text: name) do within('.ci-job-component', text: name) do
has_selector?(".ci-status-icon-#{status}", { wait: wait }.compact) has_selector?(".ci-status-icon-#{status}", { wait: wait }.compact)
end
end
end end
end
end
def has_job?(job_name) def has_job?(job_name)
has_element?(:job_link, text: job_name) has_element?(:job_link, text: job_name)
end end
def has_no_job?(job_name) def has_no_job?(job_name)
has_no_element?(:job_link, text: job_name) has_no_element?(:job_link, text: job_name)
end end
def has_tag?(tag_name) def has_tag?(tag_name)
within_element(:pipeline_badges) do within_element(:pipeline_badges) do
has_selector?('.badge', text: tag_name) has_selector?('.badge', text: tag_name)
end end
end end
def click_job(job_name) def click_job(job_name)
click_element(:job_link, text: job_name) click_element(:job_link, text: job_name)
end end
def click_linked_job(project_name) def click_linked_job(project_name)
click_element(:linked_pipeline_button, text: /#{project_name}/) click_element(:linked_pipeline_button, text: /#{project_name}/)
end end
def click_on_first_job def click_on_first_job
first('.js-pipeline-graph-job-link', wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).click first('.js-pipeline-graph-job-link', wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).click
end
end
end end
end end
end end
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class CICD < Page::Base class CICD < Page::Base
include Common include QA::Page::Settings::Common
view 'app/views/projects/settings/ci_cd/show.html.haml' do view 'app/views/projects/settings/ci_cd/show.html.haml' do
element :autodevops_settings_content element :autodevops_settings_content
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class CiVariables < Page::Base class CiVariables < Page::Base
include Common include QA::Page::Settings::Common
view 'app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue' do view 'app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue' do
element :ci_variable_key_field element :ci_variable_key_field
......
# frozen_string_literal: true
module QA
module Page
module Project
module Settings
module Common
include QA::Page::Settings::Common
end
end
end
end
end
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class GeneralPipelines < Page::Base class GeneralPipelines < Page::Base
include Common include QA::Page::Settings::Common
view 'app/views/projects/settings/ci_cd/_form.html.haml' do view 'app/views/projects/settings/ci_cd/_form.html.haml' do
element :build_coverage_regex_field element :build_coverage_regex_field
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class Main < Page::Base class Main < Page::Base
include Common include QA::Page::Settings::Common
include Component::Select2 include Component::Select2
include SubMenus::Project include SubMenus::Project
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class MergeRequest < QA::Page::Base class MergeRequest < QA::Page::Base
include Common include QA::Page::Settings::Common
view 'app/views/projects/edit.html.haml' do view 'app/views/projects/edit.html.haml' do
element :save_merge_request_changes element :save_merge_request_changes
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class Operations < Page::Base class Operations < Page::Base
include Common include QA::Page::Settings::Common
view 'app/views/projects/settings/operations/_incidents.html.haml' do view 'app/views/projects/settings/operations/_incidents.html.haml' do
element :incidents_settings_content element :incidents_settings_content
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Settings module Settings
class Repository < Page::Base class Repository < Page::Base
include Common include QA::Page::Settings::Common
view 'app/views/projects/protected_branches/shared/_index.html.haml' do view 'app/views/projects/protected_branches/shared/_index.html.haml' do
element :protected_branches_settings element :protected_branches_settings
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module CiCd module CiCd
include Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :link_pipelines element :link_pipelines
end end
......
...@@ -5,6 +5,7 @@ module QA ...@@ -5,6 +5,7 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Common module Common
extend QA::Page::PageConcern
include QA::Page::SubMenus::Common include QA::Page::SubMenus::Common
private private
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Issues module Issues
include Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :issue_boards_link element :issue_boards_link
element :issues_item element :issues_item
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Operations module Operations
include Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :operations_link element :operations_link
element :operations_environments_link element :operations_environments_link
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Project module Project
include Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :project_link element :project_link
end end
......
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Repository module Repository
include Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :project_menu_repo element :project_menu_repo
element :branches_link element :branches_link
...@@ -44,5 +48,3 @@ module QA ...@@ -44,5 +48,3 @@ module QA
end end
end end
end end
QA::Page::Project::SubMenus::Repository.prepend_if_ee('QA::EE::Page::Project::SubMenus::Repository')
...@@ -5,10 +5,14 @@ module QA ...@@ -5,10 +5,14 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Settings module Settings
include Page::Project::SubMenus::Common extend QA::Page::PageConcern
def self.included(base) def self.included(base)
super
base.class_eval do base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project.html.haml' do view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :settings_item element :settings_item
element :link_members_settings element :link_members_settings
......
# frozen_string_literal: true # frozen_string_literal: true
module QA::Page module QA
module Search module Page
class Results < QA::Page::Base module Search
view 'app/views/search/_category.html.haml' do class Results < QA::Page::Base
element :code_tab view 'app/views/search/_category.html.haml' do
element :projects_tab element :code_tab
end element :projects_tab
end
view 'app/views/search/results/_blob_data.html.haml' do view 'app/views/search/results/_blob_data.html.haml' do
element :result_item_content element :result_item_content
element :file_title_content element :file_title_content
element :file_text_content element :file_text_content
end end
view 'app/views/shared/projects/_project.html.haml' do view 'app/views/shared/projects/_project.html.haml' do
element :project element :project
end end
def switch_to_code def switch_to_code
switch_to_tab(:code_tab) switch_to_tab(:code_tab)
end end
def switch_to_projects def switch_to_projects
switch_to_tab(:projects_tab) switch_to_tab(:projects_tab)
end end
def has_file_in_project?(file_name, project_name) def has_file_in_project?(file_name, project_name)
has_element?(:result_item_content, text: "#{project_name}: #{file_name}") has_element?(:result_item_content, text: "#{project_name}: #{file_name}")
end end
def has_file_with_content?(file_name, file_text) def has_file_with_content?(file_name, file_text)
within_element_by_index(:result_item_content, 0) do within_element_by_index(:result_item_content, 0) do
break false unless has_element?(:file_title_content, text: file_name) break false unless has_element?(:file_title_content, text: file_name)
has_element?(:file_text_content, text: file_text) has_element?(:file_text_content, text: file_text)
end
end end
end
def has_project?(project_name) def has_project?(project_name)
has_element?(:project, project_name: project_name) has_element?(:project, project_name: project_name)
end end
private private
def switch_to_tab(tab) def switch_to_tab(tab)
retry_until do retry_until do
click_element(tab) click_element(tab)
has_active_element?(tab) has_active_element?(tab)
end
end end
end end
end end
......
...@@ -2,83 +2,87 @@ ...@@ -2,83 +2,87 @@
require 'rspec/core' require 'rspec/core'
module QA::Specs::Helpers module QA
module Quarantine module Specs
include RSpec::Core::Pending module Helpers
module Quarantine
include RSpec::Core::Pending
extend self extend self
def configure_rspec def configure_rspec
RSpec.configure do |config| RSpec.configure do |config|
config.before(:context, :quarantine) do config.before(:context, :quarantine) do
Quarantine.skip_or_run_quarantined_contexts(config.inclusion_filter.rules, self.class) Quarantine.skip_or_run_quarantined_contexts(config.inclusion_filter.rules, self.class)
end end
config.before do |example| config.before do |example|
Quarantine.skip_or_run_quarantined_tests_or_contexts(config.inclusion_filter.rules, example) Quarantine.skip_or_run_quarantined_tests_or_contexts(config.inclusion_filter.rules, example)
end
end
end end
end
end
# Skip the entire context if a context is quarantined. This avoids running # Skip the entire context if a context is quarantined. This avoids running
# before blocks unnecessarily. # before blocks unnecessarily.
def skip_or_run_quarantined_contexts(filters, example) def skip_or_run_quarantined_contexts(filters, example)
return unless example.metadata.key?(:quarantine) return unless example.metadata.key?(:quarantine)
skip_or_run_quarantined_tests_or_contexts(filters, example) skip_or_run_quarantined_tests_or_contexts(filters, example)
end end
# Skip tests in quarantine unless we explicitly focus on them. # Skip tests in quarantine unless we explicitly focus on them.
def skip_or_run_quarantined_tests_or_contexts(filters, example) def skip_or_run_quarantined_tests_or_contexts(filters, example)
if filters.key?(:quarantine) if filters.key?(:quarantine)
included_filters = filters_other_than_quarantine(filters) included_filters = filters_other_than_quarantine(filters)
# If :quarantine is focused, skip the test/context unless its metadata # If :quarantine is focused, skip the test/context unless its metadata
# includes quarantine and any other filters # includes quarantine and any other filters
# E.g., Suppose a test is tagged :smoke and :quarantine, and another is tagged # E.g., Suppose a test is tagged :smoke and :quarantine, and another is tagged
# :ldap and :quarantine. If we wanted to run just quarantined smoke tests # :ldap and :quarantine. If we wanted to run just quarantined smoke tests
# using `--tag quarantine --tag smoke`, without this check we'd end up # using `--tag quarantine --tag smoke`, without this check we'd end up
# running that ldap test as well because of the :quarantine metadata. # running that ldap test as well because of the :quarantine metadata.
# We could use an exclusion filter, but this way the test report will list # We could use an exclusion filter, but this way the test report will list
# the quarantined tests when they're not run so that we're aware of them # the quarantined tests when they're not run so that we're aware of them
skip("Only running tests tagged with :quarantine and any of #{included_filters.keys}") if should_skip_when_focused?(example.metadata, included_filters) skip("Only running tests tagged with :quarantine and any of #{included_filters.keys}") if should_skip_when_focused?(example.metadata, included_filters)
else else
if example.metadata.key?(:quarantine) if example.metadata.key?(:quarantine)
quarantine_message = %w(In quarantine) quarantine_message = %w(In quarantine)
quarantine_tag = example.metadata[:quarantine] quarantine_tag = example.metadata[:quarantine]
if !!quarantine_tag if !!quarantine_tag
quarantine_message << case quarantine_tag quarantine_message << case quarantine_tag
when String when String
": #{quarantine_tag}" ": #{quarantine_tag}"
when Hash when Hash
": #{quarantine_tag[:issue]}" ": #{quarantine_tag[:issue]}"
else else
'' ''
end end
end end
skip(quarantine_message.join(' ').strip) skip(quarantine_message.join(' ').strip)
end
end
end end
end
end
def filters_other_than_quarantine(filter) def filters_other_than_quarantine(filter)
filter.reject { |key, _| key == :quarantine } filter.reject { |key, _| key == :quarantine }
end end
# Checks if a test or context should be skipped. # Checks if a test or context should be skipped.
# #
# Returns true if # Returns true if
# - the metadata does not includes the :quarantine tag # - the metadata does not includes the :quarantine tag
# or if # or if
# - the metadata includes the :quarantine tag # - the metadata includes the :quarantine tag
# - and the filter includes other tags that aren't in the metadata # - and the filter includes other tags that aren't in the metadata
def should_skip_when_focused?(metadata, included_filters) def should_skip_when_focused?(metadata, included_filters)
return true unless metadata.key?(:quarantine) return true unless metadata.key?(:quarantine)
return false if included_filters.empty? return false if included_filters.empty?
(metadata.keys & included_filters.keys).empty? (metadata.keys & included_filters.keys).empty?
end
end
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment