From f84ac43cdd40b66007e2f7334a36e16709058d2b Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Fri, 4 Mar 2022 14:49:17 +0100 Subject: [PATCH] Maintenance: Switch to headless Selenium in CI. --- .gitlab/ci/base.yml | 2 + spec/support/capybara/custom_extensions.rb | 10 - spec/support/capybara/driven_by.rb | 17 +- spec/support/capybara/selenium_driver.rb | 21 +- spec/system/chat_spec.rb | 277 ++++++++---------- .../agent_organization_profile_test.rb | 4 +- test/browser/agent_user_profile_test.rb | 4 +- test/browser_test_helper.rb | 22 +- 8 files changed, 163 insertions(+), 194 deletions(-) diff --git a/.gitlab/ci/base.yml b/.gitlab/ci/base.yml index 79a2e6479..1339ce02b 100644 --- a/.gitlab/ci/base.yml +++ b/.gitlab/ci/base.yml @@ -22,6 +22,7 @@ variables: REMOTE_URL: "http://selenium-firefox:4444/wd/hub" BROWSER: "firefox" + START_XVFB: "false" # not needed for headless mode SE_NODE_MAX_SESSIONS: "5" SE_NODE_OVERRIDE_MAX_SESSIONS: "true" @@ -29,6 +30,7 @@ variables: REMOTE_URL: "http://selenium-chrome:4444/wd/hub" BROWSER: chrome + START_XVFB: "false" # not needed for headless mode SE_NODE_MAX_SESSIONS: "5" SE_NODE_OVERRIDE_MAX_SESSIONS: "true" diff --git a/spec/support/capybara/custom_extensions.rb b/spec/support/capybara/custom_extensions.rb index 05e82855c..b8a65287a 100644 --- a/spec/support/capybara/custom_extensions.rb +++ b/spec/support/capybara/custom_extensions.rb @@ -152,16 +152,6 @@ module CapybaraCustomExtensions def page(...) ZammadCapybaraSessionDelegator.new(element: super, context: self) 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 RSpec.configure do |config| diff --git a/spec/support/capybara/driven_by.rb b/spec/support/capybara/driven_by.rb index c33083671..dc1cadcad 100644 --- a/spec/support/capybara/driven_by.rb +++ b/spec/support/capybara/driven_by.rb @@ -35,10 +35,19 @@ RSpec.configure do |config| browser_height = 1000 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) 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 diff --git a/spec/support/capybara/selenium_driver.rb b/spec/support/capybara/selenium_driver.rb index 2f6091cf7..27f1f7010 100644 --- a/spec/support/capybara/selenium_driver.rb +++ b/spec/support/capybara/selenium_driver.rb @@ -8,13 +8,15 @@ Capybara.register_driver(:zammad_chrome) do |app| # Turn on browser logs options = Selenium::WebDriver::Chrome::Options.new( - logging_prefs: { + logging_prefs: { browser: 'ALL' }, - prefs: { + prefs: { 'intl.accept_languages' => 'en-US', 'profile.default_content_setting_values.notifications' => 1, # ALLOW notifications }, + # Disable the "Chrome is controlled by automation software" info bar. + excludeSwitches: ['enable-automation'], ) options = { @@ -25,6 +27,7 @@ Capybara.register_driver(:zammad_chrome) do |app| if ENV['REMOTE_URL'].present? options[:browser] = :remote options[:url] = ENV['REMOTE_URL'] + options[:options].headless! end ENV['FAKE_SELENIUM_LOGIN_USER_ID'] = nil @@ -51,6 +54,7 @@ Capybara.register_driver(:zammad_firefox) do |app| if ENV['REMOTE_URL'].present? options[:browser] = :remote options[:url] = ENV['REMOTE_URL'] + options[:options].headless! end 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? 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 diff --git a/spec/system/chat_spec.rb b/spec/system/chat_spec.rb index 9660bd246..08ba371ec 100644 --- a/spec/system/chat_spec.rb +++ b/spec/system/chat_spec.rb @@ -37,7 +37,12 @@ RSpec.describe 'Chat Handling', type: :system do end 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' 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 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_no_css('.open-zammad-chat:not([style*="display: none"]', visible: :all) + 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('.open-zammad-chat:not([style*="display: none"]', visible: :all) + 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 enable_agent_chat - open_window_and_switch - - visit chat_url - - open_chat_dialog - - switch_to_window_index(1) + using_session :customer do + visit chat_url + open_chat_dialog + end click '.active .js-acceptChat' @@ -74,14 +76,11 @@ RSpec.describe 'Chat Handling', type: :system do send_agent_message('my name is me') - switch_to_window_index(2) - - check_content('.zammad-chat .zammad-chat-agent-status', 'Online') - check_content('.zammad-chat', 'my name is me') - - send_customer_message('my name is customer') - - switch_to_window_index(1) + using_session :customer do + check_content('.zammad-chat .zammad-chat-agent-status', 'Online') + check_content('.zammad-chat', 'my name is me') + send_customer_message('my name is customer') + end check_content('.active .chat-window', 'my name is customer') 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') - switch_to_window_index(2) - - click '.js-chat-toggle .zammad-chat-header-icon' - - switch_to_window_index(1) + using_session :customer do + click '.js-chat-toggle .zammad-chat-header-icon' + end check_content('.active .chat-window', 'closed the conversation') 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 enable_agent_chat - open_window_and_switch + using_session :customer do - visit chat_url + visit chat_url - open_chat_dialog - - switch_to_window_index(1) + open_chat_dialog + end 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. click '#global-search' - switch_to_window_index(2) - - check_content('.zammad-chat .zammad-chat-agent-status', 'Online') - - send_customer_message('my name is customer') - - switch_to_window_index(1) + using_session :customer do + check_content('.zammad-chat .zammad-chat-agent-status', 'Online') + send_customer_message('my name is customer') + end expect(page).to have_css('.active .chat-window .chat-status.is-modified') 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') expect(page).to have_no_css('.active .chat-window .chat-status.is-modified') - switch_to_window_index(2) - - check_content('.zammad-chat', 'my name is me') - - switch_to_window_index(1) + using_session :customer do + check_content('.zammad-chat', 'my name is me') + end click '.active .chat-window .js-disconnect:not(.is-hidden)' 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', %r{(Chat closed by|Chat beendet von)}) + check_content('.zammad-chat .zammad-chat-agent-status', 'Offline') + 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 - - switch_to_window_index(1) + open_chat_dialog + end click '.active .js-acceptChat' @@ -163,25 +153,25 @@ RSpec.describe 'Chat Handling', type: :system do it 'open the chat', authenticated_as: :authenticate do 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) - 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' - 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) - 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' - 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) + expect(page).to have_no_css('.zammad-chat-is-open', visible: :all) + end end end @@ -189,60 +179,56 @@ RSpec.describe 'Chat Handling', type: :system do it 'check different timeouts', authenticated_as: :authenticate do enable_agent_chat - open_window_and_switch + using_session :customer do - visit chat_url + visit chat_url - # No customer action, hide the widget. - expect(page).to have_css('.zammad-chat') + # No customer action, hide the widget. + 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. - open_chat_dialog + # No agent action, show sorry screen. + open_chat_dialog - 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{(waiting|Warte)}) + check_content('.zammad-chat-modal-text', %r{(takes longer|dauert länger)}) - refresh + refresh - # No customer action, show sorry screen. - open_chat_dialog - - switch_to_window_index(1) + # No customer action, show sorry screen. + open_chat_dialog + end click '.active .js-acceptChat' 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. - switch_to_window_index(1) - click '.active .chat-window .js-close' - switch_to_window_index(2) + using_session :customer do - click '.js-restart' - - open_chat_dialog - - switch_to_window_index(1) + click '.js-restart' + open_chat_dialog + end click '.active .js-acceptChat' send_agent_message('my name is me') - switch_to_window_index(2) - - check_content('.zammad-chat', 'my name is me') + using_session :customer do + check_content('.zammad-chat', 'my name is me') + end end end @@ -254,55 +240,51 @@ RSpec.describe 'Chat Handling', type: :system do 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"}') - - switch_to_window_index(1) + check_content('.settings', '{"state":"chat_disabled"}') + end click '.content.active .js-chatSetting' 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') - check_content('.settings', '{"state":"chat_disabled"}', should_match: false) - check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}') - - switch_to_window_index(1) + expect(page).to have_no_css('.zammad-chat') + check_content('.settings', '{"state":"chat_disabled"}', should_match: false) + check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}') + end click agent_chat_switch_selector click 'a[href="#customer_chat"]' - switch_to_window_index(2) + using_session :customer do - refresh + refresh - expect(page).to have_css('.zammad-chat') - check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}', should_match: false) - check_content('.settings', '{"state":"online"}') + expect(page).to have_css('.zammad-chat') + check_content('.settings', '{"event":"chat_status_customer","data":{"state":"offline"}}', should_match: false) + 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') - check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}) - - switch_to_window_index(1) + expect(page).to have_css('.zammad-chat-is-shown') + check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}) + end 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) - - switch_to_window_index(1) + check_content('.zammad-chat-modal-text', %r{(waiting|Warte)}, should_match: false) + end expect(page).to have_no_css('.js-chatMenuItem .counter') end @@ -321,39 +303,35 @@ RSpec.describe 'Chat Handling', type: :system do modal_disappear - open_window_and_switch + using_session :customer do - visit chat_url + visit chat_url - open_chat_dialog - - switch_to_window_index(1) + open_chat_dialog + end click '.active .js-acceptChat' expect(page).to have_css('.active .chat-window .chat-status') - switch_to_window_index(2) - - check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) - - switch_to_window_index(1) + using_session :customer do + check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) + end 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') - check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) - check_content('.zammad-chat', 'my name is me') + expect(page).to have_css('.zammad-chat') + check_content('.zammad-chat', %r{(Hi Stranger|My Greeting)}) + check_content('.zammad-chat', 'my name is me') - visit "#{chat_url}#new_hash" - - switch_to_window_index(1) + visit "#{chat_url}#new_hash" + end check_content('.active .chat-window .js-body', "#{chat_url}#new_hash") end @@ -373,7 +351,7 @@ RSpec.describe 'Chat Handling', type: :system do 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' } context 'when normal mode is used' do @@ -393,13 +371,14 @@ RSpec.describe 'Chat Handling', type: :system do shared_examples 'test issue #2471' do it 'is able to close to the dialog after a idleTimeout happened' do click agent_chat_switch_selector - open_window_and_switch + using_session :customer do - visit chat_url - click '.zammad-chat .js-chat-open' - expect(page).to have_selector('.js-restart', wait: 60) - click '.js-chat-toggle .zammad-chat-header-icon' - expect(page).to have_no_selector('zammad-chat-is-open', wait: 60) + visit chat_url + click '.zammad-chat .js-chat-open' + expect(page).to have_selector('.js-restart', wait: 60) + click '.js-chat-toggle .zammad-chat-header-icon' + expect(page).to have_no_selector('zammad-chat-is-open', wait: 60) + end end end @@ -407,7 +386,7 @@ RSpec.describe 'Chat Handling', type: :system do include_examples 'test issue #2471' end - context 'wihtout jquery' do + context 'without jquery' do let(:chat_url_type) { 'znuny-no-jquery' } include_examples 'test issue #2471' diff --git a/test/browser/agent_organization_profile_test.rb b/test/browser/agent_organization_profile_test.rb index 66f1ae3a2..1e7ea4aa4 100644 --- a/test/browser/agent_organization_profile_test.rb +++ b/test/browser/agent_organization_profile_test.rb @@ -146,9 +146,7 @@ class AgentOrganizationProfileTest < TestCase css: '.active .profile [data-name="note"]', slow: true, value: message, - ) - empty_search( - browser: browser1, + blur: true ) # verify diff --git a/test/browser/agent_user_profile_test.rb b/test/browser/agent_user_profile_test.rb index 105185627..1a57df472 100644 --- a/test/browser/agent_user_profile_test.rb +++ b/test/browser/agent_user_profile_test.rb @@ -139,9 +139,7 @@ class AgentUserProfileTest < TestCase browser: browser1, css: '.active [data-name="note"]', value: message, - ) - empty_search( - browser: browser1, + blur: true, ) watch_for( diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb index e69e2bb27..3e14b3a66 100644 --- a/test/browser_test_helper.rb +++ b/test/browser_test_helper.rb @@ -91,16 +91,6 @@ class TestCase < ActiveSupport::TestCase params = { 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) @browsers[local_browser.hash] = local_browser browser_instance_preferences(local_browser) @@ -135,12 +125,24 @@ class TestCase < ActiveSupport::TestCase open_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( :remote, url: ENV['REMOTE_URL'], desired_capabilities: caps, http_client: http_client, + options: options, ) @browsers[local_browser.hash] = local_browser browser_instance_preferences(local_browser)