Commit dfce9333 authored by Dan Davison's avatar Dan Davison

Merge branch 'qa-mobile-nav-updates' into 'master'

Update page objects to handle mobile layout

See merge request gitlab-org/gitlab!66295
parents ee21fe72 e2b9b1fe
...@@ -192,9 +192,14 @@ export default { ...@@ -192,9 +192,14 @@ export default {
class="gl-sm-display-none! w-100" class="gl-sm-display-none! w-100"
block block
:text="dropdownText" :text="dropdownText"
data-qa-selector="issue_actions_dropdown"
:loading="isToggleStateButtonLoading" :loading="isToggleStateButtonLoading"
> >
<gl-dropdown-item v-if="showToggleIssueStateButton" @click="toggleIssueState"> <gl-dropdown-item
v-if="showToggleIssueStateButton"
:data-qa-selector="`mobile_${qaSelector}`"
@click="toggleIssueState"
>
{{ buttonText }} {{ buttonText }}
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath"> <gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
......
...@@ -55,6 +55,7 @@ export default { ...@@ -55,6 +55,7 @@ export default {
v-gl-tooltip="{ title: newDropdownViewModel.title }" v-gl-tooltip="{ title: newDropdownViewModel.title }"
:view-model="newDropdownViewModel" :view-model="newDropdownViewModel"
class="gl-ml-3" class="gl-ml-3"
data-qa-selector="mobile_new_dropdown"
/> />
</header> </header>
<top-nav-menu-sections class="gl-h-full" :sections="menuSections" v-on="$listeners" /> <top-nav-menu-sections class="gl-h-full" :sections="menuSections" v-on="$listeners" />
......
...@@ -46,6 +46,7 @@ export default { ...@@ -46,6 +46,7 @@ export default {
link-class="top-nav-menu-item" link-class="top-nav-menu-item"
:href="menuItem.href" :href="menuItem.href"
data-testid="item" data-testid="item"
:data-qa-selector="`${menuItem.title.toLowerCase().replace(' ', '_')}_mobile_button`"
> >
{{ menuItem.title }} {{ menuItem.title }}
</gl-dropdown-item> </gl-dropdown-item>
......
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in') - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-default btn-sign-in' = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-default btn-sign-in'
%button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: 'gl-border-none!', data: { testid: 'top-nav-responsive-toggle' } } %button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: 'gl-border-none!', data: { testid: 'top-nav-responsive-toggle', qa_selector: 'mobile_navbar_button' } }
%span.sr-only= _('Toggle navigation') %span.sr-only= _('Toggle navigation')
%span.more-icon.gl-px-3.gl-font-sm.gl-font-weight-bold %span.more-icon.gl-px-3.gl-font-sm.gl-font-weight-bold
%span.gl-pr-2= _('Menu') %span.gl-pr-2= _('Menu')
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%nav.breadcrumbs{ class: [container, @content_class], 'aria-label': _('Breadcrumbs') } %nav.breadcrumbs{ class: [container, @content_class], 'aria-label': _('Breadcrumbs') }
.breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) } .breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
- if defined?(@left_sidebar) - if defined?(@left_sidebar)
= button_tag class: 'toggle-mobile-nav', type: 'button' do = button_tag class: 'toggle-mobile-nav', data: { qa_selector: 'toggle_mobile_nav_button' }, type: 'button' do
%span.sr-only= _("Open sidebar") %span.sr-only= _("Open sidebar")
= sprite_icon('hamburger', size: 18) = sprite_icon('hamburger', size: 18)
.breadcrumbs-links{ data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } } .breadcrumbs-links{ data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } }
......
...@@ -485,3 +485,109 @@ To run the LDAP tests on your local with TLS disabled, follow these steps: ...@@ -485,3 +485,109 @@ To run the LDAP tests on your local with TLS disabled, follow these steps:
```shell ```shell
GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_DEBUG=true WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All http://localhost qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb GITLAB_LDAP_USERNAME="tanuki" GITLAB_LDAP_PASSWORD="password" QA_DEBUG=true WEBDRIVER_HEADLESS=false bin/qa Test::Instance::All http://localhost qa/specs/features/browser_ui/1_manage/login/log_into_gitlab_via_ldap_spec.rb
``` ```
## Guide to the mobile suite
### What are mobile tests
Tests that are tagged with `:mobile` can be run against specified mobile devices using cloud emulator/simulator services.
### How to run mobile tests with Sauce Labs
Running directly against an environment like staging is not recommended because Sauce Labs test logs expose credentials. Therefore, it is best practice and the default to use a tunnel.
Tunnel installation instructions are here [https://docs.saucelabs.com/secure-connections/sauce-connect/installation]. To start the tunnel, after following the installation above, copy the run command in Sauce Labs > Tunnels (must be logged in to Sauce Labs with the credentials found in 1Password) and run in terminal.
NOTE:
It is highly recommended to use `GITLAB_QA_ACCESS_TOKEN` to speed up tests and reduce flakiness.
`QA_REMOTE_MOBILE_DEVICE_NAME` can be any device name listed in [https://saucelabs.com/platform/supported-browsers-devices] under Emulators/simulators and the latest versions of Android or iOS. `QA_BROWSER` must be set to `safari` for iOS devices and `chrome` for Android devices.
1. To test against a local instance with a tunnel running, in `gitlab/qa` run:
```shell
$ QA_BROWSER="safari" \
QA_REMOTE_MOBILE_DEVICE_NAME="iPhone 12 Simulator" \
QA_REMOTE_GRID="ondemand.saucelabs.com:80" \
QA_REMOTE_GRID_USERNAME="gitlab-sl" \
QA_REMOTE_GRID_ACCESS_KEY="<found in Sauce Lab account>" \
GITLAB_QA_ACCESS_TOKEN="<token>" \
bundle exec bin/qa Test::Instance::All http://<local_ip>:3000 -- <relative_spec_path>
```
Results can be watched in real time while logged into Sauce Labs under AUTOMATED > Test Results.
### How to add an existing test to the mobile suite
The main reason a test might fail when adding the `:mobile` tag is navigation differences in desktop vs mobile layouts, therefore the test needs to be updated to use mobile navigation when running mobile tests.
If an existing method needs to be changed or a new one created, a new mobile page object should be created in `qa/qa/mobile/page/` and it should be prepended in the original page object by adding:
```ruby
prepend Mobile::Page::NewPageObject if Runtime::Env.mobile_layout?
```
For example to change an existing method when running mobile tests:
New mobile page object:
```ruby
module QA
module Mobile
module Page
module Project
module Show
extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
prepend QA::Mobile::Page::Main::Menu
view 'app/assets/javascripts/nav/components/top_nav_new_dropdown.vue' do
element :new_issue_mobile_button
end
end
end
def go_to_new_issue
open_mobile_new_dropdown
click_element(:new_issue_mobile_button)
end
end
end
end
end
end
```
Original page object prepending the new mobile if there's a mobile layout:
```ruby
module QA
module Page
module Project
class Show < Page::Base
prepend Mobile::Page::Project::Show if Runtime::Env.mobile_layout?
view 'app/views/layouts/header/_new_dropdown.html.haml' do
element :new_menu_toggle
end
view 'app/helpers/nav/new_dropdown_helper.rb' do
element :new_issue_link
end
def go_to_new_issue
click_element(:new_menu_toggle)
click_element(:new_issue_link)
end
end
end
end
end
```
When running mobile tests for phone layouts, both `remote_mobile_device_name` and `mobile_layout` are `true` but when using a tablet layout, only `remote_mobile_device_name` is true. This is because phone layouts have more menus closed by default such as how both tablets and phones have the left nav closed but unlike phone layouts, tablets have the regular top navigation bar, not the mobile one. So in the case where the navigation being edited needs to be used in tablet layouts as well, use `remote_mobile_device_name` instead of `mobile_layout?` when prepending so it will use it if it's a tablet layout as well.
...@@ -23,8 +23,11 @@ module QA ...@@ -23,8 +23,11 @@ module QA
end end
def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false) def sign_in(as: nil, address: :gitlab, skip_page_validation: false, admin: false)
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?) unless Page::Main::Login.perform(&:on_login_page?)
Runtime::Browser.visit(address, Page::Main::Login) Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?)
Runtime::Browser.visit(address, Page::Main::Login)
end
Page::Main::Login.perform do |login| Page::Main::Login.perform do |login|
if admin if admin
login.sign_in_using_admin_credentials login.sign_in_using_admin_credentials
......
# frozen_string_literal: true
module QA
module Mobile
module Page
module Main
module Menu
extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
view 'app/views/layouts/header/_default.html.haml' do
element :mobile_navbar_button, required: true
end
view 'app/assets/javascripts/nav/components/responsive_home.vue' do
element :mobile_new_dropdown
end
end
end
def open_mobile_menu
if has_no_element?(:user_avatar)
Support::Retrier.retry_until do
click_element(:mobile_navbar_button)
has_element?(:user_avatar)
end
end
end
def open_mobile_new_dropdown
open_mobile_menu
Support::Retrier.retry_until do
find('[data-qa-selector="mobile_new_dropdown"] > button').click
has_css?('.dropdown-menu-right.show')
end
end
def has_personal_area?(wait: Capybara.default_max_wait_time)
open_mobile_menu
super
end
def has_no_personal_area?(wait: Capybara.default_max_wait_time)
open_mobile_menu
super
end
def within_user_menu
open_mobile_menu
super
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Mobile
module Page
module Profile
module Menu
extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
prepend QA::Mobile::Page::Main::Menu
end
end
def within_sidebar
open_mobile_nav_sidebar
super
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Mobile
module Page
module Project
module Issue
module Show
extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
view 'app/assets/javascripts/issue_show/components/header_actions.vue' do
element :issue_actions_dropdown
element :mobile_close_issue_button
element :mobile_reopen_issue_button
end
end
end
def click_close_issue_button
find('[data-qa-selector="issue_actions_dropdown"] > button').click
find_element(:mobile_close_issue_button, visible: false).click
end
def has_reopen_issue_button?
find('[data-qa-selector="issue_actions_dropdown"] > button').click
has_element?(:mobile_reopen_issue_button)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Mobile
module Page
module Project
module Show
extend QA::Page::PageConcern
def self.prepended(base)
super
base.class_eval do
prepend QA::Mobile::Page::Main::Menu
view 'app/assets/javascripts/nav/components/top_nav_new_dropdown.vue' do
element :new_issue_mobile_button
end
end
end
def go_to_new_issue
open_mobile_new_dropdown
click_element(:new_issue_mobile_button)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Mobile
module Page
module SubMenus
module Common
def open_mobile_nav_sidebar
if has_element?(:project_sidebar, visible: false)
Support::Retrier.retry_until do
click_element(:toggle_mobile_nav_button)
has_element?(:project_sidebar, visible: true)
end
end
end
def within_sidebar
wait_for_requests
open_mobile_nav_sidebar
super
end
end
end
end
end
end
...@@ -45,6 +45,10 @@ module QA ...@@ -45,6 +45,10 @@ module QA
has_element?(:sign_in_button) has_element?(:sign_in_button)
end end
def on_login_page?
has_element?(:login_page, wait: 0)
end
def sign_in_using_credentials(user: nil, skip_page_validation: false) def sign_in_using_credentials(user: nil, skip_page_validation: false)
# Don't try to log-in if we're already logged-in # Don't try to log-in if we're already logged-in
return if Page::Main::Menu.perform(&:signed_in?) return if Page::Main::Menu.perform(&:signed_in?)
...@@ -164,6 +168,8 @@ module QA ...@@ -164,6 +168,8 @@ module QA
fill_element :password_field, user.password fill_element :password_field, user.password
click_element :sign_in_button click_element :sign_in_button
Support::WaitForRequests.wait_for_requests
Page::Main::Terms.perform do |terms| Page::Main::Terms.perform do |terms|
terms.accept_terms if terms.visible? terms.accept_terms if terms.visible?
end end
......
...@@ -4,6 +4,8 @@ module QA ...@@ -4,6 +4,8 @@ module QA
module Page module Page
module Main module Main
class Menu < Page::Base class Menu < Page::Base
prepend Mobile::Page::Main::Menu if Runtime::Env.mobile_layout?
view 'app/views/layouts/header/_current_user_dropdown.html.haml' do view 'app/views/layouts/header/_current_user_dropdown.html.haml' do
element :sign_out_link element :sign_out_link
element :edit_profile_link element :edit_profile_link
...@@ -12,12 +14,12 @@ module QA ...@@ -12,12 +14,12 @@ module QA
view 'app/views/layouts/header/_default.html.haml' do view 'app/views/layouts/header/_default.html.haml' do
element :navbar, required: true element :navbar, required: true
element :canary_badge_link element :canary_badge_link
element :user_avatar, required: true element :user_avatar, required: !QA::Runtime::Env.mobile_layout?
element :user_menu, required: true element :user_menu, required: !QA::Runtime::Env.mobile_layout?
element :stop_impersonation_link element :stop_impersonation_link
element :issues_shortcut_button, required: true element :issues_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
element :merge_requests_shortcut_button, required: true element :merge_requests_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
element :todos_shortcut_button, required: true element :todos_shortcut_button, required: !QA::Runtime::Env.mobile_layout?
end end
view 'app/assets/javascripts/nav/components/top_nav_app.vue' do view 'app/assets/javascripts/nav/components/top_nav_app.vue' do
...@@ -98,10 +100,14 @@ module QA ...@@ -98,10 +100,14 @@ module QA
end end
def signed_in? def signed_in?
return false if Page::Main::Login.perform(&:on_login_page?)
has_personal_area?(wait: 0) has_personal_area?(wait: 0)
end end
def not_signed_in? def not_signed_in?
return true if Page::Main::Login.perform(&:on_login_page?)
has_no_personal_area? has_no_personal_area?
end end
...@@ -115,7 +121,7 @@ module QA ...@@ -115,7 +121,7 @@ module QA
click_element :sign_out_link click_element :sign_out_link
end end
has_no_element?(:user_avatar) not_signed_in?
end end
end end
......
...@@ -4,6 +4,10 @@ module QA ...@@ -4,6 +4,10 @@ module QA
module Page module Page
module Profile module Profile
class Menu < Page::Base class Menu < Page::Base
# We need to check remote_mobile_device_name instead of mobile_layout? here
# since tablets have the regular top navigation bar but still close the left nav
prepend QA::Mobile::Page::Profile::Menu if QA::Runtime::Env.remote_mobile_device_name
view 'app/views/layouts/nav/sidebar/_profile.html.haml' do view 'app/views/layouts/nav/sidebar/_profile.html.haml' do
element :access_token_link, 'link_to profile_personal_access_tokens_path' # rubocop:disable QA/ElementWithPattern element :access_token_link, 'link_to profile_personal_access_tokens_path' # rubocop:disable QA/ElementWithPattern
element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern element :access_token_title, 'Access Tokens' # rubocop:disable QA/ElementWithPattern
......
...@@ -9,6 +9,7 @@ module QA ...@@ -9,6 +9,7 @@ module QA
include Page::Component::Note include Page::Component::Note
include Page::Component::DesignManagement include Page::Component::DesignManagement
include Page::Component::Issuable::Sidebar include Page::Component::Issuable::Sidebar
prepend Mobile::Page::Project::Issue::Show if Runtime::Env.mobile_layout?
view 'app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue' do view 'app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue' do
element :remove_related_issue_button element :remove_related_issue_button
...@@ -64,6 +65,10 @@ module QA ...@@ -64,6 +65,10 @@ module QA
def has_metrics_unfurled? def has_metrics_unfurled?
has_element?(:prometheus_graph_widgets, wait: 30) has_element?(:prometheus_graph_widgets, wait: 30)
end end
def has_reopen_issue_button?
has_element?(:reopen_issue_button)
end
end end
end end
end end
......
...@@ -9,6 +9,7 @@ module QA ...@@ -9,6 +9,7 @@ module QA
include Page::Component::Breadcrumbs include Page::Component::Breadcrumbs
include Page::Project::SubMenus::Settings include Page::Project::SubMenus::Settings
include Page::File::Shared::CommitMessage include Page::File::Shared::CommitMessage
prepend Mobile::Page::Project::Show if Runtime::Env.mobile_layout?
view 'app/assets/javascripts/repository/components/preview/index.vue' do view 'app/assets/javascripts/repository/components/preview/index.vue' do
element :blob_viewer_content element :blob_viewer_content
...@@ -117,7 +118,7 @@ module QA ...@@ -117,7 +118,7 @@ module QA
end end
def go_to_new_issue def go_to_new_issue
click_element :new_menu_toggle click_element(:new_menu_toggle)
click_element(:new_issue_link) click_element(:new_issue_link)
end end
......
...@@ -19,6 +19,10 @@ module QA ...@@ -19,6 +19,10 @@ module QA
view 'app/views/shared/nav/_sidebar_menu.html.haml' do view 'app/views/shared/nav/_sidebar_menu.html.haml' do
element :sidebar_menu_link element :sidebar_menu_link
end end
view 'app/views/layouts/nav/_breadcrumbs.html.haml' do
element :toggle_mobile_nav_button
end
end end
end end
......
...@@ -4,6 +4,10 @@ module QA ...@@ -4,6 +4,10 @@ module QA
module Page module Page
module SubMenus module SubMenus
module Common module Common
# We need to check remote_mobile_device_name instead of mobile_layout? here
# since tablets have the regular top navigation bar but still close the left nav
prepend Mobile::Page::SubMenus::Common if QA::Runtime::Env.remote_mobile_device_name
def hover_element(element) def hover_element(element)
within_sidebar do within_sidebar do
find_element(element).hover find_element(element).hover
......
...@@ -205,6 +205,9 @@ module QA ...@@ -205,6 +205,9 @@ module QA
simulate_slow_connection if Runtime::Env.simulate_slow_connection? simulate_slow_connection if Runtime::Env.simulate_slow_connection?
# Wait until the new page is ready for us to interact with it
Support::WaitForRequests.wait_for_requests
page_class.validate_elements_present! if page_class.respond_to?(:validate_elements_present!) page_class.validate_elements_present! if page_class.respond_to?(:validate_elements_present!)
if QA::Runtime::Env.qa_cookies if QA::Runtime::Env.qa_cookies
......
...@@ -153,6 +153,12 @@ module QA ...@@ -153,6 +153,12 @@ module QA
ENV['QA_REMOTE_MOBILE_DEVICE_NAME'] ENV['QA_REMOTE_MOBILE_DEVICE_NAME']
end end
def mobile_layout?
return false if ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].blank?
!(ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].downcase.include?('ipad') || ENV['QA_REMOTE_MOBILE_DEVICE_NAME'].downcase.include?('tablet'))
end
def user_username def user_username
ENV['GITLAB_USERNAME'] ENV['GITLAB_USERNAME']
end end
......
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
RSpec.describe 'Manage', :smoke do RSpec.describe 'Manage', :smoke, :mobile do
describe 'basic user login' do describe 'basic user login' do
it 'user logs in using basic credentials and logs out', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1578' do it 'user logs in using basic credentials and logs out', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1578' do
Flow::Login.sign_in Flow::Login.sign_in
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
Flow::Login.sign_in Flow::Login.sign_in
end end
it 'creates an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1185' do it 'creates an issue', :mobile, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1185' do
issue = Resource::Issue.fabricate_via_browser_ui! issue = Resource::Issue.fabricate_via_browser_ui!
Page::Project::Menu.perform(&:click_issues) Page::Project::Menu.perform(&:click_issues)
...@@ -19,13 +19,13 @@ module QA ...@@ -19,13 +19,13 @@ module QA
end end
end end
it 'closes an issue', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1222' do it 'closes an issue', :mobile, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1222' do
closed_issue.visit! closed_issue.visit!
Page::Project::Issue::Show.perform do |issue_page| Page::Project::Issue::Show.perform do |issue_page|
issue_page.click_close_issue_button issue_page.click_close_issue_button
expect(issue_page).to have_element(:reopen_issue_button) expect(issue_page).to have_reopen_issue_button
end end
Page::Project::Menu.perform(&:click_issues) Page::Project::Menu.perform(&:click_issues)
......
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