Maintenance: Switch to headless Selenium in CI.

This commit is contained in:
Martin Gruner 2022-03-04 14:49:17 +01:00
parent 25c971ed00
commit f84ac43cdd
8 changed files with 163 additions and 194 deletions

View file

@ -22,6 +22,7 @@
variables: variables:
REMOTE_URL: "http://selenium-firefox:4444/wd/hub" REMOTE_URL: "http://selenium-firefox:4444/wd/hub"
BROWSER: "firefox" BROWSER: "firefox"
START_XVFB: "false" # not needed for headless mode
SE_NODE_MAX_SESSIONS: "5" SE_NODE_MAX_SESSIONS: "5"
SE_NODE_OVERRIDE_MAX_SESSIONS: "true" SE_NODE_OVERRIDE_MAX_SESSIONS: "true"
@ -29,6 +30,7 @@
variables: variables:
REMOTE_URL: "http://selenium-chrome:4444/wd/hub" REMOTE_URL: "http://selenium-chrome:4444/wd/hub"
BROWSER: chrome BROWSER: chrome
START_XVFB: "false" # not needed for headless mode
SE_NODE_MAX_SESSIONS: "5" SE_NODE_MAX_SESSIONS: "5"
SE_NODE_OVERRIDE_MAX_SESSIONS: "true" SE_NODE_OVERRIDE_MAX_SESSIONS: "true"

View file

@ -152,16 +152,6 @@ module CapybaraCustomExtensions
def page(...) def page(...)
ZammadCapybaraSessionDelegator.new(element: super, context: self) ZammadCapybaraSessionDelegator.new(element: super, context: self)
end end
# Work around an issue with failures in Capybara if different specs of the same rspec run
# use using_session with the same name, e.g. :second_browser. Solve by prepending the test name.
def using_session(name_or_session, &block)
if !(name_or_session.is_a? Capybara::Session)
# self.class.name refers to the RSpec test case.
name_or_session = (self.class.name + "::#{name_or_session}").to_sym
end
Capybara.using_session(name_or_session, &block)
end
end end
RSpec.configure do |config| RSpec.configure do |config|

View file

@ -35,10 +35,19 @@ RSpec.configure do |config|
browser_height = 1000 browser_height = 1000
end end
# Firefox and Chrome effective screen sizes are slightly different
# accomodate that by reducing declared screen size on Firefox
browser_height -= 44 if browser_name == 'firefox'
page.driver.browser.manage.window.resize_to(browser_width, browser_height) page.driver.browser.manage.window.resize_to(browser_width, browser_height)
end end
config.after(:each, type: :system) do
# Make sure additional sessions (from using_sessions) are always ended
# after every test and not kept alive. Selenium will automatically close
# idle sessions which can cause 404 errors later.
# (see https://github.com/teamcapybara/capybara/issues/2237)
Capybara.send(:session_pool).reverse_each do |_mode, session|
if !session.eql?(Capybara.current_session)
session.quit
end
end
end
end end

View file

@ -8,13 +8,15 @@ Capybara.register_driver(:zammad_chrome) do |app|
# Turn on browser logs # Turn on browser logs
options = Selenium::WebDriver::Chrome::Options.new( options = Selenium::WebDriver::Chrome::Options.new(
logging_prefs: { logging_prefs: {
browser: 'ALL' browser: 'ALL'
}, },
prefs: { prefs: {
'intl.accept_languages' => 'en-US', 'intl.accept_languages' => 'en-US',
'profile.default_content_setting_values.notifications' => 1, # ALLOW notifications 'profile.default_content_setting_values.notifications' => 1, # ALLOW notifications
}, },
# Disable the "Chrome is controlled by automation software" info bar.
excludeSwitches: ['enable-automation'],
) )
options = { options = {
@ -25,6 +27,7 @@ Capybara.register_driver(:zammad_chrome) do |app|
if ENV['REMOTE_URL'].present? if ENV['REMOTE_URL'].present?
options[:browser] = :remote options[:browser] = :remote
options[:url] = ENV['REMOTE_URL'] options[:url] = ENV['REMOTE_URL']
options[:options].headless!
end end
ENV['FAKE_SELENIUM_LOGIN_USER_ID'] = nil ENV['FAKE_SELENIUM_LOGIN_USER_ID'] = nil
@ -51,6 +54,7 @@ Capybara.register_driver(:zammad_firefox) do |app|
if ENV['REMOTE_URL'].present? if ENV['REMOTE_URL'].present?
options[:browser] = :remote options[:browser] = :remote
options[:url] = ENV['REMOTE_URL'] options[:url] = ENV['REMOTE_URL']
options[:options].headless!
end end
ENV['FAKE_SELENIUM_LOGIN_USER_ID'] = nil ENV['FAKE_SELENIUM_LOGIN_USER_ID'] = nil
@ -60,16 +64,3 @@ Capybara.register_driver(:zammad_firefox) do |app|
driver.browser.file_detector = nil if ENV['REMOTE_URL'].present? driver.browser.file_detector = nil if ENV['REMOTE_URL'].present?
end end
end end
class Capybara::Selenium::Driver
alias original_quit quit
def quit
original_quit
rescue Selenium::WebDriver::Error::ServerError
# Work around a possible capybara/Selenium bug. driver.quit() fails because there is already no session any more;
# not sure why that happens.
ensure
@browser = nil
end
end

View file

@ -37,7 +37,12 @@ RSpec.describe 'Chat Handling', type: :system do
end end
def send_agent_message(message) def send_agent_message(message)
find('.active .chat-window .js-customerChatInput').send_keys(message) input = find('.active .chat-window .js-customerChatInput')
input.send_keys(message)
# Work around an obsure bug of send_keys sometimes not working on Firefox headless.
if input.text != message
input.execute_script("this.textContent = '#{message}'")
end
click '.active .chat-window .js-send' click '.active .chat-window .js-send'
end end
@ -45,13 +50,13 @@ RSpec.describe 'Chat Handling', type: :system do
it 'check that button is hidden after idle timeout', authenticated_as: :authenticate do it 'check that button is hidden after idle timeout', authenticated_as: :authenticate do
click agent_chat_switch_selector click agent_chat_switch_selector
open_window_and_switch using_session :customer do
visit chat_url
visit chat_url expect(page).to have_css('.zammad-chat', visible: :all)
expect(page).to have_css('.zammad-chat-is-hidden', visible: :all)
expect(page).to have_css('.zammad-chat', visible: :all) expect(page).to have_no_css('.open-zammad-chat:not([style*="display: none"]', visible: :all)
expect(page).to have_css('.zammad-chat-is-hidden', visible: :all) end
expect(page).to have_no_css('.open-zammad-chat:not([style*="display: none"]', visible: :all)
end end
end end
@ -59,13 +64,10 @@ RSpec.describe 'Chat Handling', type: :system do
it 'messages in each direction, starting on agent side', authenticated_as: :authenticate do it 'messages in each direction, starting on agent side', authenticated_as: :authenticate do
enable_agent_chat enable_agent_chat
open_window_and_switch using_session :customer do
visit chat_url
visit chat_url open_chat_dialog
end
open_chat_dialog
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
@ -74,14 +76,11 @@ RSpec.describe 'Chat Handling', type: :system do
send_agent_message('my name is me') send_agent_message('my name is me')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat .zammad-chat-agent-status', 'Online')
check_content('.zammad-chat .zammad-chat-agent-status', 'Online') check_content('.zammad-chat', 'my name is me')
check_content('.zammad-chat', 'my name is me') send_customer_message('my name is customer')
end
send_customer_message('my name is customer')
switch_to_window_index(1)
check_content('.active .chat-window', 'my name is customer') check_content('.active .chat-window', 'my name is customer')
expect(page).to have_css('.active .chat-window .chat-status.is-modified') expect(page).to have_css('.active .chat-window .chat-status.is-modified')
@ -90,11 +89,9 @@ RSpec.describe 'Chat Handling', type: :system do
expect(page).to have_no_css('.active .chat-window .chat-status.is-modified') expect(page).to have_no_css('.active .chat-window .chat-status.is-modified')
switch_to_window_index(2) using_session :customer do
click '.js-chat-toggle .zammad-chat-header-icon'
click '.js-chat-toggle .zammad-chat-header-icon' end
switch_to_window_index(1)
check_content('.active .chat-window', 'closed the conversation') check_content('.active .chat-window', 'closed the conversation')
end end
@ -102,13 +99,12 @@ RSpec.describe 'Chat Handling', type: :system do
it 'messages in each direction, starting on customer side', authenticated_as: :authenticate do it 'messages in each direction, starting on customer side', authenticated_as: :authenticate do
enable_agent_chat enable_agent_chat
open_window_and_switch using_session :customer do
visit chat_url visit chat_url
open_chat_dialog open_chat_dialog
end
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
@ -117,13 +113,10 @@ RSpec.describe 'Chat Handling', type: :system do
# Keep focus outside of chat window to check .chat-status.is-modified later. # Keep focus outside of chat window to check .chat-status.is-modified later.
click '#global-search' click '#global-search'
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat .zammad-chat-agent-status', 'Online')
check_content('.zammad-chat .zammad-chat-agent-status', 'Online') send_customer_message('my name is customer')
end
send_customer_message('my name is customer')
switch_to_window_index(1)
expect(page).to have_css('.active .chat-window .chat-status.is-modified') expect(page).to have_css('.active .chat-window .chat-status.is-modified')
check_content('.active .chat-window', 'my name is customer') check_content('.active .chat-window', 'my name is customer')
@ -131,27 +124,24 @@ RSpec.describe 'Chat Handling', type: :system do
send_agent_message('my name is me') send_agent_message('my name is me')
expect(page).to have_no_css('.active .chat-window .chat-status.is-modified') expect(page).to have_no_css('.active .chat-window .chat-status.is-modified')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat', 'my name is me')
check_content('.zammad-chat', 'my name is me') end
switch_to_window_index(1)
click '.active .chat-window .js-disconnect:not(.is-hidden)' click '.active .chat-window .js-disconnect:not(.is-hidden)'
click '.active .chat-window .js-close' click '.active .chat-window .js-close'
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat .zammad-chat-agent-status', 'Offline') check_content('.zammad-chat .zammad-chat-agent-status', 'Offline')
check_content('.zammad-chat', %r{(Chat closed by|Chat beendet von)}) check_content('.zammad-chat', %r{(Chat closed by|Chat beendet von)})
click '.zammad-chat .js-chat-toggle .zammad-chat-header-icon' click '.zammad-chat .js-chat-toggle .zammad-chat-header-icon'
expect(page).to have_no_css('.zammad-chat-is-open') expect(page).to have_no_css('.zammad-chat-is-open')
open_chat_dialog open_chat_dialog
end
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
@ -163,25 +153,25 @@ RSpec.describe 'Chat Handling', type: :system do
it 'open the chat', authenticated_as: :authenticate do it 'open the chat', authenticated_as: :authenticate do
enable_agent_chat enable_agent_chat
open_window_and_switch using_session :customer do
visit chat_url
visit chat_url expect(page).to have_css('.zammad-chat', visible: :all)
expect(page).to have_css('.zammad-chat-is-hidden', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-shown', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-open', visible: :all)
expect(page).to have_css('.zammad-chat', visible: :all) click '.open-zammad-chat'
expect(page).to have_css('.zammad-chat-is-hidden', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-shown', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-open', visible: :all)
click '.open-zammad-chat' expect(page).to have_css('.zammad-chat-is-shown', visible: :all)
expect(page).to have_css('.zammad-chat-is-open', visible: :all)
check_content('.zammad-chat-modal-text', %r{(waiting|Warte)})
expect(page).to have_css('.zammad-chat-is-shown', visible: :all) click '.zammad-chat-header-icon-close'
expect(page).to have_css('.zammad-chat-is-open', visible: :all)
check_content('.zammad-chat-modal-text', %r{(waiting|Warte)})
click '.zammad-chat-header-icon-close' expect(page).to have_no_css('.zammad-chat-is-shown', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-open', visible: :all)
expect(page).to have_no_css('.zammad-chat-is-shown', visible: :all) end
expect(page).to have_no_css('.zammad-chat-is-open', visible: :all)
end end
end end
@ -189,60 +179,56 @@ RSpec.describe 'Chat Handling', type: :system do
it 'check different timeouts', authenticated_as: :authenticate do it 'check different timeouts', authenticated_as: :authenticate do
enable_agent_chat enable_agent_chat
open_window_and_switch using_session :customer do
visit chat_url visit chat_url
# No customer action, hide the widget. # No customer action, hide the widget.
expect(page).to have_css('.zammad-chat') expect(page).to have_css('.zammad-chat')
expect(page).to have_no_css('.zammad-chat') expect(page).to have_no_css('.zammad-chat')
refresh refresh
# No agent action, show sorry screen. # No agent action, show sorry screen.
open_chat_dialog open_chat_dialog
check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}) check_content('.zammad-chat-modal-text', %r{(waiting|Warte)})
check_content('.zammad-chat-modal-text', %r{(takes longer|dauert länger)}) check_content('.zammad-chat-modal-text', %r{(takes longer|dauert länger)})
refresh refresh
# No customer action, show sorry screen. # No customer action, show sorry screen.
open_chat_dialog open_chat_dialog
end
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
send_agent_message('agent is asking') send_agent_message('agent is asking')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat', 'agent is asking') check_content('.zammad-chat', 'agent is asking')
check_content('.zammad-chat-modal-text', %r{(Since you didn't respond|Da Sie in den letzten)}, wait: 30) check_content('.zammad-chat-modal-text', %r{(Since you didn't respond|Da Sie in den letzten)}, wait: 30)
end
# Test the restart of inactive chat. # Test the restart of inactive chat.
switch_to_window_index(1)
click '.active .chat-window .js-close' click '.active .chat-window .js-close'
switch_to_window_index(2) using_session :customer do
click '.js-restart' click '.js-restart'
open_chat_dialog
open_chat_dialog end
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
send_agent_message('my name is me') send_agent_message('my name is me')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat', 'my name is me')
check_content('.zammad-chat', 'my name is me') end
end end
end end
@ -254,55 +240,51 @@ RSpec.describe 'Chat Handling', type: :system do
expect(page).to have_no_css(agent_chat_switch_selector) expect(page).to have_no_css(agent_chat_switch_selector)
open_window_and_switch using_session :customer do
visit chat_url visit chat_url
check_content('.settings', '{"state":"chat_disabled"}') check_content('.settings', '{"state":"chat_disabled"}')
end
switch_to_window_index(1)
click '.content.active .js-chatSetting' click '.content.active .js-chatSetting'
expect(page).to have_css(agent_chat_switch_selector) expect(page).to have_css(agent_chat_switch_selector)
switch_to_window_index(2) using_session :customer do
refresh refresh
expect(page).to have_no_css('.zammad-chat') expect(page).to have_no_css('.zammad-chat')
check_content('.settings', '{"state":"chat_disabled"}', should_match: false) check_content('.settings', '{"state":"chat_disabled"}', should_match: false)
check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}') check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}')
end
switch_to_window_index(1)
click agent_chat_switch_selector click agent_chat_switch_selector
click 'a[href="#customer_chat"]' click 'a[href="#customer_chat"]'
switch_to_window_index(2) using_session :customer do
refresh refresh
expect(page).to have_css('.zammad-chat') expect(page).to have_css('.zammad-chat')
check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}', should_match: false) check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}', should_match: false)
check_content('.settings', '{"state":"online"}') check_content('.settings', '{"state":"online"}')
click '.zammad-chat .js-chat-open' click '.zammad-chat .js-chat-open'
expect(page).to have_css('.zammad-chat-is-shown') expect(page).to have_css('.zammad-chat-is-shown')
check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}) check_content('.zammad-chat-modal-text', %r{(waiting|Warte)})
end
switch_to_window_index(1)
check_content('.js-chatMenuItem .counter', '1') check_content('.js-chatMenuItem .counter', '1')
switch_to_window_index(2) using_session :customer do
click '.zammad-chat .js-chat-toggle .zammad-chat-header-icon' click '.zammad-chat .js-chat-toggle .zammad-chat-header-icon'
check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}, should_match: false) check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}, should_match: false)
end
switch_to_window_index(1)
expect(page).to have_no_css('.js-chatMenuItem .counter') expect(page).to have_no_css('.js-chatMenuItem .counter')
end end
@ -321,39 +303,35 @@ RSpec.describe 'Chat Handling', type: :system do
modal_disappear modal_disappear
open_window_and_switch using_session :customer do
visit chat_url visit chat_url
open_chat_dialog open_chat_dialog
end
switch_to_window_index(1)
click '.active .js-acceptChat' click '.active .js-acceptChat'
expect(page).to have_css('.active .chat-window .chat-status') expect(page).to have_css('.active .chat-window .chat-status')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)})
check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) end
switch_to_window_index(1)
send_agent_message('my name is me') send_agent_message('my name is me')
switch_to_window_index(2) using_session :customer do
check_content('.zammad-chat', 'my name is me') check_content('.zammad-chat', 'my name is me')
refresh refresh
expect(page).to have_css('.zammad-chat') expect(page).to have_css('.zammad-chat')
check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)})
check_content('.zammad-chat', 'my name is me') check_content('.zammad-chat', 'my name is me')
visit "#{chat_url}#new_hash" visit "#{chat_url}#new_hash"
end
switch_to_window_index(1)
check_content('.active .chat-window .js-body', "#{chat_url}#new_hash") check_content('.active .chat-window .js-body', "#{chat_url}#new_hash")
end end
@ -373,7 +351,7 @@ RSpec.describe 'Chat Handling', type: :system do
end end
end end
context 'when none jquery variant is used' do context 'when no-jquery variant is used' do
let(:chat_url_type) { 'znuny-no-jquery' } let(:chat_url_type) { 'znuny-no-jquery' }
context 'when normal mode is used' do context 'when normal mode is used' do
@ -393,13 +371,14 @@ RSpec.describe 'Chat Handling', type: :system do
shared_examples 'test issue #2471' do shared_examples 'test issue #2471' do
it 'is able to close to the dialog after a idleTimeout happened' do it 'is able to close to the dialog after a idleTimeout happened' do
click agent_chat_switch_selector click agent_chat_switch_selector
open_window_and_switch using_session :customer do
visit chat_url visit chat_url
click '.zammad-chat .js-chat-open' click '.zammad-chat .js-chat-open'
expect(page).to have_selector('.js-restart', wait: 60) expect(page).to have_selector('.js-restart', wait: 60)
click '.js-chat-toggle .zammad-chat-header-icon' click '.js-chat-toggle .zammad-chat-header-icon'
expect(page).to have_no_selector('zammad-chat-is-open', wait: 60) expect(page).to have_no_selector('zammad-chat-is-open', wait: 60)
end
end end
end end
@ -407,7 +386,7 @@ RSpec.describe 'Chat Handling', type: :system do
include_examples 'test issue #2471' include_examples 'test issue #2471'
end end
context 'wihtout jquery' do context 'without jquery' do
let(:chat_url_type) { 'znuny-no-jquery' } let(:chat_url_type) { 'znuny-no-jquery' }
include_examples 'test issue #2471' include_examples 'test issue #2471'

View file

@ -146,9 +146,7 @@ class AgentOrganizationProfileTest < TestCase
css: '.active .profile [data-name="note"]', css: '.active .profile [data-name="note"]',
slow: true, slow: true,
value: message, value: message,
) blur: true
empty_search(
browser: browser1,
) )
# verify # verify

View file

@ -139,9 +139,7 @@ class AgentUserProfileTest < TestCase
browser: browser1, browser: browser1,
css: '.active [data-name="note"]', css: '.active [data-name="note"]',
value: message, value: message,
) blur: true,
empty_search(
browser: browser1,
) )
watch_for( watch_for(

View file

@ -91,16 +91,6 @@ class TestCase < ActiveSupport::TestCase
params = { params = {
profile: profile, profile: profile,
} }
if ENV['BROWSER_HEADLESS'].present?
case browser
when 'firefox'
params[:options] = Selenium::WebDriver::Firefox::Options.new
params[:options].add_argument('-headless')
when 'chrome'
params[:options] = Selenium::WebDriver::Chrome::Options.new
params[:options].add_argument('-headless')
end
end
local_browser = Selenium::WebDriver.for(browser.to_sym, params) local_browser = Selenium::WebDriver.for(browser.to_sym, params)
@browsers[local_browser.hash] = local_browser @browsers[local_browser.hash] = local_browser
browser_instance_preferences(local_browser) browser_instance_preferences(local_browser)
@ -135,12 +125,24 @@ class TestCase < ActiveSupport::TestCase
open_timeout: 120, open_timeout: 120,
read_timeout: 120 read_timeout: 120
) )
case browser
when 'firefox'
options = Selenium::WebDriver::Firefox::Options.new
options.headless!
when 'chrome'
options = Selenium::WebDriver::Chrome::Options.new(
# Disable the "Chrome is controlled by automation software" info bar.
excludeSwitches: ['enable-automation'],
)
options.headless!
end
local_browser = Selenium::WebDriver.for( local_browser = Selenium::WebDriver.for(
:remote, :remote,
url: ENV['REMOTE_URL'], url: ENV['REMOTE_URL'],
desired_capabilities: caps, desired_capabilities: caps,
http_client: http_client, http_client: http_client,
options: options,
) )
@browsers[local_browser.hash] = local_browser @browsers[local_browser.hash] = local_browser
browser_instance_preferences(local_browser) browser_instance_preferences(local_browser)