Maintenance: Stabilize tests with general wait_for_ajax_empty approach and some fixed tests.

This commit is contained in:
Rolf Schmidt 2021-06-15 06:26:52 +00:00 committed by Thorsten Eckel
parent 1242121de7
commit c44196d7ab
24 changed files with 415 additions and 199 deletions

View file

@ -690,6 +690,7 @@ Metrics/ModuleLength:
- 'lib/signature_detection.rb'
- 'lib/static_assets.rb'
- 'lib/transaction_dispatcher.rb'
- 'spec/support/capybara/common_actions.rb'
Metrics/PerceivedComplexity:
Exclude:

View file

@ -1038,12 +1038,13 @@ class App.ControllerTable extends App.Controller
# update store
@preferencesStore('order', 'customOrderBy', @orderBy)
@preferencesStore('order', 'customOrderDirection', @orderDirection)
render = =>
@renderTableFull(false, skipHeadersResize: true)
App.QueueManager.add('tableRender', render)
if @sortRenderCallback
App.QueueManager.add('tableRender', @sortRenderCallback)
else
render = =>
@renderTableFull(false, skipHeadersResize: true)
App.QueueManager.add('tableRender', render)
App.QueueManager.run('tableRender')

View file

@ -68,9 +68,11 @@ module BrowserTestHelper
# await_empty_ajax_queue
#
def await_empty_ajax_queue
wait(5, interval: 0.5).until_constant do
wait(5, interval: 0.1).until_constant do
page.evaluate_script('App.Ajax.queue().length').zero?
end
rescue
nil
end
# Moves the mouse from its current position by the given offset.
@ -112,6 +114,7 @@ module BrowserTestHelper
#
def release_mouse
page.driver.browser.action.release.perform
await_empty_ajax_queue
end
class Waiter < SimpleDelegator

View file

@ -38,6 +38,8 @@ module CommonActions
wait(4).until_exists do
current_login
end
await_empty_ajax_queue
end
# Checks if the current session is logged in.
@ -120,6 +122,16 @@ module CommonActions
route = "/##{route}"
end
super(route)
# wait for AJAX requets only on WebApp visits
return if !route.start_with?('/#')
return if route == '/#logout'
# make sure all AJAX requests are done
await_empty_ajax_queue
# make sure loading is completed (e.g. ticket zoom may take longer)
expect(page).to have_no_css('.icon-loading', wait: 30)
end
# Overwrites the global Capybara.always_include_port setting (true)

View file

@ -34,3 +34,108 @@ class Capybara::Node::Element
raise "Element still moving after #{checks} checks"
end
end
module ZammadCapybarActionDelegator
def select(*)
super.tap do
await_empty_ajax_queue
end
end
def click(*)
super.tap do
await_empty_ajax_queue
end
end
def click_on(*)
super.tap do
await_empty_ajax_queue
end
end
def click_link_or_button(*)
super.tap do
await_empty_ajax_queue
end
end
def click_button(*)
super.tap do
await_empty_ajax_queue
end
end
def select_option(*)
super.tap do
await_empty_ajax_queue
end
end
def send_keys(*)
super.tap do
await_empty_ajax_queue
end
end
end
module ZammadCapybarSelectorDelegator
def find_field(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def find_button(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def find_by_id(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def find_link(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def find(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def first(*)
ZammadCapybaraElementDelegator.new(element: super, context: self)
end
def all(*)
super.map { |element| ZammadCapybaraElementDelegator.new(element: element, context: self) }
end
end
class ZammadCapybaraSessionDelegator < SimpleDelegator
extend Forwardable
def_delegator :@context, :await_empty_ajax_queue
include ZammadCapybarSelectorDelegator
def initialize(context:, element:)
@context = context
super(element)
end
end
class ZammadCapybaraElementDelegator < ZammadCapybaraSessionDelegator
include ZammadCapybarActionDelegator
end
module CapybaraCustomExtensions
include ZammadCapybarActionDelegator
include ZammadCapybarSelectorDelegator
def page(*)
ZammadCapybaraSessionDelegator.new(element: super, context: self)
end
end
RSpec.configure do |config|
config.include CapybaraCustomExtensions, type: :system
end

View file

@ -15,12 +15,12 @@ RSpec.describe 'Authentication', type: :system do
it 'Logout' do
logout
expect_current_route 'login', wait: 2
expect_current_route 'login', wait: 10
end
it 'will unset user attributes after logout' do
logout
expect_current_route 'login', wait: 2
expect_current_route 'login', wait: 10
visit '/#signup'
@ -31,20 +31,20 @@ RSpec.describe 'Authentication', type: :system do
it 'Login and redirect to requested url', authenticated_as: false do
visit 'ticket/zoom/1'
expect_current_route 'login', wait: 2
expect_current_route 'login', wait: 10
login(
username: 'master@example.com',
password: 'test',
)
expect_current_route 'ticket/zoom/1', wait: 2
expect_current_route 'ticket/zoom/1', wait: 10
end
it 'Login and redirect to requested url via external authentication', authenticated_as: false do
visit 'ticket/zoom/1'
expect_current_route 'login', wait: 2
expect_current_route 'login', wait: 10
# simulate jump to external ressource
visit 'https://www.zammad.org'
@ -59,7 +59,7 @@ RSpec.describe 'Authentication', type: :system do
# jump back and check if origin requested url is shown
visit ''
expect_current_route 'ticket/zoom/1', wait: 2
expect_current_route 'ticket/zoom/1', wait: 10
expect(current_login).to eq('master@example.com')
end

View file

@ -30,7 +30,6 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do
fill_in 'Lastname', with: 'Braun'
fill_in 'Email', with: 'nick.braun@zammad.org'
click_on 'Invite'
await_empty_ajax_queue
expect(User.find_by(firstname: 'Nick').roles).to eq([Role.find_by(name: 'Public')])
end
end
@ -42,6 +41,7 @@ RSpec.describe 'Dashboard', type: :system, authenticated_as: true do
before do
ensure_websocket(check_if_pinged: false)
sleep 3 # fast relog causes raise conditions in websocket server
end
context 'Logout by frontend plugin - Default', authenticated_as: :authenticate do

View file

@ -1,46 +1,46 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
RSpec.shared_examples 'pagination' do |model:, klass:, path:, sort_by: :name|
def prepare(model)
let(:model) { model }
def authenticate
create_list(model, 500)
true
end
it 'does paginate' do
prepare(model)
it 'does paginate', authenticated_as: :authenticate do
visit path
refresh # more stability
expect(page).to have_css('.js-pager', wait: 10)
class_page1 = klass.order(sort_by => :asc, id: :asc).offset(50).first
expect(page).to have_text(class_page1.name, wait: 10)
expect(page).to have_css('.js-page.is-selected', text: '1')
page.first('.js-page', text: '2').click
await_empty_ajax_queue
page.first('.js-page', text: '2', wait: 10).click
class_page2 = klass.order(sort_by => :asc, id: :asc).offset(175).first
expect(page).to have_text(class_page2.name, wait: 10)
expect(page).to have_css('.js-page.is-selected', text: '2')
page.first('.js-page', text: '3').click
await_empty_ajax_queue
page.first('.js-page', text: '3', wait: 10).click
class_page3 = klass.order(sort_by => :asc, id: :asc).offset(325).first
expect(page).to have_text(class_page3.name, wait: 10)
expect(page).to have_css('.js-page.is-selected', text: '3')
page.first('.js-page', text: '4').click
await_empty_ajax_queue
page.first('.js-page', text: '4', wait: 10).click
class_page4 = klass.order(sort_by => :asc, id: :asc).offset(475).first
expect(page).to have_text(class_page4.name, wait: 10)
expect(page).to have_css('.js-page.is-selected', text: '4')
page.first('.js-page', text: '1').click
await_empty_ajax_queue
page.first('.js-page', text: '1', wait: 10).click
page.first('.js-tableHead[data-column-key=name]').click
await_empty_ajax_queue
expect(page).to have_text(class_page1.name, wait: 10)
expect(page).to have_css('.js-page.is-selected', text: '1')
page.first('.js-tableHead[data-column-key=name]').click
await_empty_ajax_queue
class_last = klass.order(sort_by => :desc).first
expect(page).to have_text(class_last.name, wait: 10)
end

View file

@ -42,12 +42,12 @@ RSpec.describe 'Manage > Users', type: :system do
visit 'manage/users'
within(:active_content) do
row = find("tr[data-id=\"#{user.id}\"]")
row = find("tr[data-id=\"#{user.id}\"]", wait: 10)
row.find('.js-action').click
row.find('.js-switchTo').click
end
await_empty_ajax_queue
expect(page).to have_text("Zammad looks like this for \"#{user.firstname} #{user.lastname}\"", wait: 10)
end
end
end

View file

@ -63,8 +63,6 @@ RSpec.describe 'Admin Panel > Objects', type: :system, authenticated_as: true do
'nulloption' => true,
'maxlength' => 255 }
await_empty_ajax_queue
expect(ObjectManager::Attribute.last.data_option).to eq(expected_data_options)
end
end

View file

@ -15,6 +15,6 @@ RSpec.describe 'System > Translations', type: :system do
click '.js-syncChanges'
modal_ready && modal_disappear # make sure test is not terminated while modal is visible
modal_disappear # make sure test is not terminated while modal is visible
end
end

View file

@ -204,8 +204,6 @@ RSpec.describe 'Ticket Create', type: :system do
encrypt_button = find('.js-securityEncrypt', wait: 5)
sign_button = find('.js-securitySign', wait: 5)
await_empty_ajax_queue
active_button_class = '.btn--active'
expect(encrypt_button.matches_css?(active_button_class, wait: 2)).to be(encrypt)
expect(sign_button.matches_css?(active_button_class, wait: 2)).to be(sign)
@ -234,8 +232,6 @@ RSpec.describe 'Ticket Create', type: :system do
within(:active_content) do
use_template(template)
await_empty_ajax_queue
select new_group.name, from: 'group_id'
end
end
@ -378,7 +374,6 @@ RSpec.describe 'Ticket Create', type: :system do
click_on 'Link issue'
fill_in 'link', with: ENV['GITLAB_ISSUE_LINK']
click_on 'Submit'
await_empty_ajax_queue
# verify issue
content = find('.sidebar-git-issue-content')
@ -390,7 +385,6 @@ RSpec.describe 'Ticket Create', type: :system do
# create Ticket
click '.js-submit'
await_empty_ajax_queue
# check stored data
expect(Ticket.last.preferences[:gitlab][:issue_links][0]).to eq(ENV['GITLAB_ISSUE_LINK'])
@ -432,7 +426,6 @@ RSpec.describe 'Ticket Create', type: :system do
click_on 'Link issue'
fill_in 'link', with: ENV['GITHUB_ISSUE_LINK']
click_on 'Submit'
await_empty_ajax_queue
# verify issue
content = find('.sidebar-git-issue-content')
@ -444,7 +437,6 @@ RSpec.describe 'Ticket Create', type: :system do
# create Ticket
click '.js-submit'
await_empty_ajax_queue
# check stored data
expect(Ticket.last.preferences[:github][:issue_links][0]).to eq(ENV['GITHUB_ISSUE_LINK'])

View file

@ -35,7 +35,7 @@ RSpec.describe 'Ticket Update', type: :system do
select('closed', from: 'state_id')
click('.js-attributeBar .js-submit')
expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]', wait: 2)
expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]', wait: 10)
end
# the update should have failed and thus the ticket is still in the new state
@ -45,7 +45,7 @@ RSpec.describe 'Ticket Update', type: :system do
# update should work now
find(".edit [name=#{attribute.name}]").select('name 2')
click('.js-attributeBar .js-submit')
expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]', wait: 2)
expect(page).to have_no_css('.js-submitDropdown .js-submit[disabled]', wait: 10)
end
ticket.reload
@ -185,7 +185,7 @@ RSpec.describe 'Ticket Update', type: :system do
it 'tickets history of both tickets should show the merge event' do
visit "#ticket/zoom/#{origin_ticket.id}"
within(:active_content) do
expect(page).to have_css('.js-actions .dropdown-toggle', wait: 3)
expect(page).to have_css('.js-actions .dropdown-toggle', wait: 10)
click '.js-actions .dropdown-toggle'
click '.js-actions .dropdown-menu [data-type="ticket-history"]'

View file

@ -112,9 +112,9 @@ RSpec.describe 'Ticket views', type: :system do
release_mouse
await_empty_ajax_queue
expect(Ticket.first.articles.last.subject).to eq('macro note')
expect do
wait(10, interval: 0.1).until { Ticket.first.articles.last.subject == 'macro note' }
end.not_to raise_error
end
end
end
@ -136,12 +136,9 @@ RSpec.describe 'Ticket views', type: :system do
click '.js-submit'
end
await_empty_ajax_queue
expect([
ticket1.articles.last&.body,
ticket2.articles.last&.body
]).to be_all note
expect do
wait(10, interval: 0.1).until { [ ticket1.articles.last&.body, ticket2.articles.last&.body ] == [note, note] }
end.not_to raise_error
end
end
@ -184,9 +181,9 @@ RSpec.describe 'Ticket views', type: :system do
it 'does basic view test of tickets' do
visit 'ticket/view/my_tickets'
expect(page).to have_text(ticket.title)
expect(page).to have_text(ticket.title, wait: 10)
click_on 'My Organization Tickets'
expect(page).to have_text(ticket.title)
expect(page).to have_text(ticket.title, wait: 10)
end
end
end

View file

@ -448,7 +448,7 @@ RSpec.describe 'Ticket zoom', type: :system do
end
context 'button is hidden on the go' do
let(:setting_delete_timeframe) { 5 }
let(:setting_delete_timeframe) { 10 }
let(:user) { agent }
let(:item) { 'article_note_self' }
@ -692,8 +692,6 @@ RSpec.describe 'Ticket zoom', type: :system do
encrypt_button = find('.js-securityEncrypt', wait: 5)
sign_button = find('.js-securitySign', wait: 5)
await_empty_ajax_queue
active_button_class = '.btn--active'
expect(encrypt_button.matches_css?(active_button_class, wait: 2)).to be(encrypt)
expect(sign_button.matches_css?(active_button_class, wait: 2)).to be(sign)
@ -709,8 +707,6 @@ RSpec.describe 'Ticket zoom', type: :system do
within(:active_content) do
all('a[data-type=emailReply]').last.click
find('.articleNewEdit-body').send_keys('Test')
await_empty_ajax_queue
end
end
@ -726,8 +722,6 @@ RSpec.describe 'Ticket zoom', type: :system do
all('a[data-type=emailReply]').last.click
find('.articleNewEdit-body').send_keys('Test')
await_empty_ajax_queue
select new_group.name, from: 'group_id'
end
end
@ -883,8 +877,6 @@ RSpec.describe 'Ticket zoom', type: :system do
find(:richtext).execute_script "this.innerHTML = \"#{ticket_article_body}\""
find('.js-submit').click
end
await_empty_ajax_queue
end
def forward
@ -893,8 +885,6 @@ RSpec.describe 'Ticket zoom', type: :system do
fill_in 'To', with: 'customer@example.com'
find('.js-submit').click
end
await_empty_ajax_queue
end
def images_identical?(image_a, image_b)
@ -986,7 +976,7 @@ RSpec.describe 'Ticket zoom', type: :system do
visit "ticket/zoom/#{ticket.id}"
refresh # refresh to have assets generated for ticket
expect(page).to have_select('state_id', options: %w[new open closed])
expect(page).to have_select('state_id', options: %w[new open closed], wait: 10)
expect(page).to have_no_select('priority_id')
expect(page).to have_no_select('owner_id')
expect(page).to have_no_css('div.tabsSidebar-tab[data-tab=customer]')
@ -1119,11 +1109,10 @@ RSpec.describe 'Ticket zoom', type: :system do
# make sure ticket is done loading and change will be pushed via WS
find(:active_ticket_article, ticket_article)
await_empty_ajax_queue
ticket_note.update!(internal: false)
expect(page).to have_selector(:active_ticket_article, ticket_note)
expect(page).to have_selector(:active_ticket_article, ticket_note, wait: 10)
end
end
end
@ -1190,37 +1179,37 @@ RSpec.describe 'Ticket zoom', type: :system do
it 'previous is not clickable for the first item' do
open_nth_item(0)
expect { click '.pagination .previous' }.not_to change { current_url }
expect { click '.pagination .previous' }.not_to change { page.find('.content.active')[:id] }
end
it 'next is clickable for the first item' do
open_nth_item(0)
expect { click '.pagination .next' }.to change { current_url }
expect { click '.pagination .next' }.to change { page.find('.content.active')[:id] }
end
it 'previous is clickable for the middle item' do
open_nth_item(1)
expect { click '.pagination .previous' }.to change { current_url }
expect { click '.pagination .previous' }.to change { page.find('.content.active')[:id] }
end
it 'next is clickable for the middle item' do
open_nth_item(1)
expect { click '.pagination .next' }.to change { current_url }
expect { click '.pagination .next' }.to change { page.find('.content.active')[:id] }
end
it 'previous is clickable for the last item' do
open_nth_item(2)
expect { click '.pagination .previous' }.to change { current_url }
expect { click '.pagination .previous' }.to change { page.find('.content.active')[:id] }
end
it 'next is not clickable for the last item' do
open_nth_item(2)
expect { click '.pagination .next' }.not_to change { current_url }
expect { click '.pagination .next' }.not_to change { page.find('.content.active')[:id] }
end
def open_nth_item(nth)
@ -1243,8 +1232,6 @@ RSpec.describe 'Ticket zoom', type: :system do
visit "ticket/zoom/#{ticket_a.id}"
await_empty_ajax_queue
visit 'ticket/view/all_unassigned'
end
@ -1252,7 +1239,7 @@ RSpec.describe 'Ticket zoom', type: :system do
within :active_content do
click_on ticket_a.title
expect(page).to have_css('.pagination-counter')
expect(page).to have_css('.pagination-counter', wait: 10)
end
end
@ -1262,7 +1249,7 @@ RSpec.describe 'Ticket zoom', type: :system do
visit 'dashboard'
visit "ticket/zoom/#{ticket_a.id}"
expect(page).to have_css('.pagination-counter')
expect(page).to have_css('.pagination-counter', wait: 10)
end
end
end
@ -1293,7 +1280,6 @@ RSpec.describe 'Ticket zoom', type: :system do
# wait for article to be added to the page
expect(page).to have_css('.ticket-article-item', count: 1)
await_empty_ajax_queue
# create a on-the-fly article with attachment that will get pushed to open browser
article1 = create(:ticket_article, ticket: ticket)
@ -1310,7 +1296,6 @@ RSpec.describe 'Ticket zoom', type: :system do
# wait for article to be added to the page
expect(page).to have_css('.ticket-article-item', count: 2, wait: 10)
await_empty_ajax_queue
# click on forward of created article
within :active_ticket_article, article1 do
@ -1327,7 +1312,6 @@ RSpec.describe 'Ticket zoom', type: :system do
click '.js-submit'
# wait for article to be added to the page
await_empty_ajax_queue
expect(page).to have_css('.ticket-article-item', count: 3)
# check if attachment was forwarded successfully
@ -1376,7 +1360,6 @@ RSpec.describe 'Ticket zoom', type: :system do
it 'not shown to customer' do
visit "ticket/zoom/#{ticket.id}"
await_empty_ajax_queue
within :active_content do
expect(page).to have_no_css('.controls[data-name=pending_time]')
@ -1389,7 +1372,6 @@ RSpec.describe 'Ticket zoom', type: :system do
ticket.update(pending_time: 1.day.from_now, state: Ticket::State.lookup(name: 'pending reminder'))
visit "ticket/zoom/#{ticket.id}"
await_empty_ajax_queue
end
let(:ticket) { Ticket.first }
@ -1436,7 +1418,6 @@ RSpec.describe 'Ticket zoom', type: :system do
ensure_websocket do
visit "ticket/zoom/#{ticket.id}"
await_empty_ajax_queue
within :active_ticket_article, article do
expect(page).to have_css(%(a[href="#{url}"]))
@ -1457,44 +1438,33 @@ RSpec.describe 'Ticket zoom', type: :system do
end
end
def check_obscured(top: true, middle: true, bottom: true, scroll_y: 0)
expect(page).to have_text(ticket.title, wait: 10)
wait(5, interval: 0.2).until do
scroll_y != find('.ticketZoom').native.location.y
end
expect(page).to have_css("div#article-content-#{article_at_the_top.id}", obscured: top, wait: 10)
expect(page).to have_css("div#article-content-#{article_in_the_middle.id}", obscured: middle, wait: 10)
expect(page).to have_css("div#article-content-#{article_at_the_bottom.id}", obscured: bottom, wait: 10)
find('.ticketZoom').native.location.y
end
it 'scrolls to given Article ID' do
ensure_websocket do
visit "ticket/zoom/#{ticket.id}/#{article_in_the_middle.id}"
await_empty_ajax_queue
# workaround because browser scrolls in test initially to the bottom
# maybe because the articles are not present?!
refresh
visit "ticket/zoom/#{ticket.id}"
y = check_obscured(bottom: false)
# scroll to article in the middle of the page
within :active_content do
find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
expect(find("div#article-content-#{article_at_the_top.id}")).to be_obscured
expect(find("div#article-content-#{article_in_the_middle.id}")).not_to be_obscured
expect(find("div#article-content-#{article_at_the_bottom.id}")).to be_obscured
end
visit "ticket/zoom/#{ticket.id}/#{article_in_the_middle.id}"
y = check_obscured(middle: false, scroll_y: y)
# scroll to article at the top of the page
visit "ticket/zoom/#{ticket.id}/#{article_at_the_top.id}"
await_empty_ajax_queue
within :active_content do
find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
expect(find("div#article-content-#{article_at_the_top.id}")).not_to be_obscured
expect(find("div#article-content-#{article_in_the_middle.id}")).to be_obscured
expect(find("div#article-content-#{article_at_the_bottom.id}")).to be_obscured
end
y = check_obscured(top: false, scroll_y: y)
# scroll to article at the bottom of the page
visit "ticket/zoom/#{ticket.id}/#{article_at_the_bottom.id}"
await_empty_ajax_queue
within :active_content do
find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
expect(find("div#article-content-#{article_at_the_top.id}")).to be_obscured
expect(find("div#article-content-#{article_in_the_middle.id}")).to be_obscured
expect(find("div#article-content-#{article_at_the_bottom.id}")).not_to be_obscured
end
check_obscured(bottom: false, scroll_y: y)
end
end
end
@ -1503,12 +1473,11 @@ RSpec.describe 'Ticket zoom', type: :system do
it 'will properly show the "See more" link if you switch between the ticket and the dashboard on new articles' do
ensure_websocket do
visit "ticket/zoom/#{ticket.id}"
await_empty_ajax_queue
visit 'dashboard'
expect(page).to have_css("a.js-dashboardMenuItem[data-key='Dashboard'].is-active", wait: 10)
article_id = create(:'ticket/article', ticket: ticket, body: "#{SecureRandom.uuid} #{"lorem ipsum\n" * 200}")
expect(page).to have_css('div.tasks a.is-modified', wait: 10)
expect(page).to have_css('div.tasks a.is-modified', wait: 30)
visit "ticket/zoom/#{ticket.id}"
within :active_content do
@ -1533,7 +1502,6 @@ RSpec.describe 'Ticket zoom', type: :system do
visit "ticket/zoom/#{ticket.id}"
find('.js-openDropdownMacro').click
find(:macro, macro.id).click
await_empty_ajax_queue
expect(ticket.reload.articles.last.body).to eq(macro_body)
expect(ticket.reload.articles.last.content_type).to eq('text/html')
@ -1598,7 +1566,6 @@ RSpec.describe 'Ticket zoom', type: :system do
click_on 'Link issue'
fill_in 'link', with: ENV['GITLAB_ISSUE_LINK']
click_on 'Submit'
await_empty_ajax_queue
# verify issue
content = find('.sidebar-git-issue-content')
@ -1615,7 +1582,6 @@ RSpec.describe 'Ticket zoom', type: :system do
# delete issue
click(".sidebar-git-issue-delete span[data-issue-id='#{ENV['GITLAB_ISSUE_LINK']}']")
await_empty_ajax_queue
content = find('.sidebar[data-tab=gitlab] .sidebar-content')
expect(content).to have_text('No linked issues')
@ -1658,7 +1624,6 @@ RSpec.describe 'Ticket zoom', type: :system do
click_on 'Link issue'
fill_in 'link', with: ENV['GITHUB_ISSUE_LINK']
click_on 'Submit'
await_empty_ajax_queue
# verify issue
content = find('.sidebar-git-issue-content')
@ -1675,7 +1640,6 @@ RSpec.describe 'Ticket zoom', type: :system do
# delete issue
click(".sidebar-git-issue-delete span[data-issue-id='#{ENV['GITHUB_ISSUE_LINK']}']")
await_empty_ajax_queue
content = find('.sidebar[data-tab=github] .sidebar-content')
expect(content).to have_text('No linked issues')
@ -1707,7 +1671,6 @@ RSpec.describe 'Ticket zoom', type: :system do
expect(page).to have_text(ticket_open.title, wait: 20)
visit "#ticket/zoom/#{ticket_open.id}"
click '.tabsSidebar-tab[data-tab=customer]'
click '.user-tickets[data-type=closed]'
expect(page).to have_text(ticket_closed.title, wait: 20)
end

View file

@ -159,7 +159,7 @@ class AgentTicketActionLevel0Test < TestCase
css: '.newTicket',
value: 'New Ticket',
)
exists_not(css: '.newTicket select[name="group_id"]')
exists(css: '.newTicket .form-group.hide select[name="group_id"]')
set(
css: '.newTicket input[name="title"]',

View file

@ -30,14 +30,7 @@ class AgentTicketAttachmentTest < TestCase
sleep 1
# submit form
click(css: '.content.active .js-submit')
sleep 2
# check warning
alert = @browser.switch_to.alert
alert.dismiss()
#alert.accept()
#alert = alert.text
click(css: '.content.active .js-submit', expect_alert: true)
# since selenium webdriver with firefox is not able to upload files, skipp here
# https://github.com/w3c/webdriver/issues/1230
@ -90,12 +83,7 @@ class AgentTicketAttachmentTest < TestCase
)
# submit form
click(css: '.content.active .js-submit')
sleep 2
# check warning
alert = @browser.switch_to.alert
alert.dismiss()
click(css: '.content.active .js-submit', expect_alert: true)
# add attachment, attachment check should quiet
file_upload(
@ -111,12 +99,6 @@ class AgentTicketAttachmentTest < TestCase
# submit form
click(css: '.content.active .js-submit')
sleep 2
# no warning
#alert = @browser.switch_to.alert
# check if article exists
# discard changes should gone away
watch_for_disappear(

View file

@ -160,7 +160,12 @@ class AgentTicketMacroTest < TestCase
# when we re-enter the Zoom view for a ticket via the overview
tasks_close_all()
sleep 8 # to update overview list to open correct/next ticket in overview
overview_open(
link: '#ticket/view/all_unassigned',
)
await_text(text: ticket1[:title])
await_text(text: ticket2[:title])
ticket_open_by_overview(
title: ticket1[:title],

View file

@ -67,6 +67,9 @@ class AgentTicketOverviewLevel0Test < TestCase
css: %(.content.active table tr td input[value="#{ticket2[:id]}"][type="checkbox"]:checked),
)
# remember current overview count
overview_counter_before = overview_counter()
# select close state & submit
select(
css: '.content.active .bulkAction [name="state_id"]',
@ -92,6 +95,7 @@ class AgentTicketOverviewLevel0Test < TestCase
)
# remember current overview count
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'] - 2)
overview_counter_before = overview_counter()
# click options and enable number and article count
@ -188,11 +192,9 @@ class AgentTicketOverviewLevel0Test < TestCase
body: 'overview count test #3',
}
)
sleep 6
# get new overview count
overview_counter_new = overview_counter()
assert_equal(overview_counter_before['#ticket/view/all_unassigned'] + 1, overview_counter_new['#ticket/view/all_unassigned'])
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'] + 1)
# open ticket by search
ticket_open_by_search(
@ -206,11 +208,9 @@ class AgentTicketOverviewLevel0Test < TestCase
state: 'closed',
}
)
sleep 6
# get current overview count
overview_counter_after = overview_counter()
assert_equal(overview_counter_before['#ticket/view/all_unassigned'], overview_counter_after['#ticket/view/all_unassigned'])
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'])
# cleanup
tasks_close_all()
@ -334,8 +334,7 @@ class AgentTicketOverviewLevel0Test < TestCase
)
# get new overview count
overview_counter_new = overview_counter()
assert_equal(overview_counter_before['#ticket/view/all_unassigned'] - 2, overview_counter_new['#ticket/view/all_unassigned'])
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'] - 2)
# open ticket by search
ticket_open_by_search(
@ -463,8 +462,7 @@ class AgentTicketOverviewLevel0Test < TestCase
)
# get new overview count
overview_counter_new = overview_counter()
assert_equal(overview_counter_before['#ticket/view/all_unassigned'] - 2, overview_counter_new['#ticket/view/all_unassigned'])
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'] - 2)
# cleanup
tasks_close_all()

View file

@ -3,6 +3,20 @@
require 'browser_test_helper'
class AgentTicketOverviewTabTest < TestCase
def task_count_equals(count)
retries ||= 0
assert_equal(count, @browser.find_elements(css: '.tasks .task').count)
rescue
retries += 1
if retries < 5
sleep 1
retry
end
raise e
end
def test_i
@browser = browser_instance
login(
@ -54,7 +68,7 @@ class AgentTicketOverviewTabTest < TestCase
link: '#ticket/view/all_unassigned',
)
assert_equal(1, @browser.find_elements(css: '.tasks .task').count)
task_count_equals(1)
ticket_update(
data: {
@ -70,7 +84,7 @@ class AgentTicketOverviewTabTest < TestCase
timeout: 8,
)
assert_equal(1, @browser.find_elements(css: '.tasks .task').count)
task_count_equals(1)
ticket_update(
data: {
@ -80,7 +94,7 @@ class AgentTicketOverviewTabTest < TestCase
task_type: 'closeTab', # default: stayOnTab / possible: closeTab, closeNextInOverview, stayOnTab
)
assert_equal(0, @browser.find_elements(css: '.tasks .task').count)
task_count_equals(0)
# cleanup
tasks_close_all()

View file

@ -4,8 +4,9 @@ require 'browser_test_helper'
class AgentUserManageTest < TestCase
def test_agent_customer_ticket_create
customer_user_email = "customer-test-#{rand(999_999)}@example.com"
firstname = 'Customer Firstname'
random_number = rand(999_999)
customer_user_email = "customer-test-#{random_number}@example.com"
firstname = "Customer Firstname #{random_number}"
lastname = 'Customer Lastname'
fullname = "#{firstname} #{lastname} <#{customer_user_email}>"
@ -21,10 +22,10 @@ class AgentUserManageTest < TestCase
click(css: 'a[href="#new"]', only_if_exists: true)
click(css: 'a[href="#ticket/create"]')
watch_for(
css: '.content.active .newTicket',
timeout: 1,
)
await_text(text: 'New Ticket')
# Rumors say there is a modal reaper which will kill your modals if you dont sleep before a new ticket create
sleep 3
click(css: '.content.active .newTicket [name="customer_id_completion"]')
@ -77,11 +78,13 @@ class AgentUserManageTest < TestCase
css: '.content.active .newTicket input[name="customer_id_completion"]',
value: fullname,
)
sleep 4
# call new ticket screen again
tasks_close_all()
# wait for user get indexed in elastic search
await_global_search(query: random_number)
click(css: 'a[href="#new"]', only_if_exists: true)
click(css: 'a[href="#ticket/create"]')

View file

@ -160,13 +160,11 @@ class FormTest < TestCase
value: "some text\nnew line",
)
click(
browser: customer,
css: 'body div.zammad-form-modal button[type="submit"]',
browser: customer,
css: 'body div.zammad-form-modal button[type="submit"]',
expect_alert: true,
)
# check warning
alert = customer.switch_to.alert
alert.dismiss()
sleep 10
# fill form invalid data - within correct time

View file

@ -28,7 +28,8 @@ class SwitchToUserTest < TestCase
css: '.content.active .dropdown--actions',
)
click(
css: '.content.active .icon-switchView',
css: '.content.active .icon-switchView',
ajax: false
)
sleep 3
@ -42,7 +43,7 @@ class SwitchToUserTest < TestCase
)
login = @browser.find_elements({ css: '.user-menu .user a' })[0].attribute('title')
assert_equal(login, 'nicole.braun@zammad.org')
click(css: '.switchBackToUser .js-close')
click(css: '.switchBackToUser .js-close', ajax: false)
sleep 5
login = @browser.find_elements({ css: '.user-menu .user a' })[0].attribute('title')

View file

@ -542,6 +542,12 @@ class TestCase < ActiveSupport::TestCase
sleep 0.2 if !params[:fast]
sleep params[:wait] if params[:wait]
if params[:expect_alert]
check_alert(params)
else
await_empty_ajax_queue(params)
end
end
=begin
@ -825,6 +831,7 @@ class TestCase < ActiveSupport::TestCase
end
sleep 0.2
await_empty_ajax_queue(params)
end
=begin
@ -880,6 +887,8 @@ class TestCase < ActiveSupport::TestCase
dropdown.select_by(:text, params[:value])
#puts "select2 - #{params.inspect}"
end
await_empty_ajax_queue(params)
end
=begin
@ -1265,16 +1274,17 @@ set type of task (closeTab, closeNextInOverview, stayOnTab)
=end
def verify_task(params = {}, fallback = false)
def verify_task(params = {})
switch_window_focus(params)
log('verify_task', params)
instance = params[:browser] || @browser
data = params[:data]
sleep 1
begin
retries ||= 0
sleep 1
# verify title
if data[:title]
title = instance.find_elements(css: '.tasks .is-active')[0].text.strip
@ -1318,10 +1328,8 @@ set type of task (closeTab, closeNextInOverview, stayOnTab)
end
end
rescue => e
# just try again
if !fallback
verify_task(params, true)
end
retries += 1
retry if retries < 5
raise "ERROR: #{e.inspect}"
end
true
@ -1572,6 +1580,7 @@ wait untill text in selector disabppears
.key_up(:control)
.perform
screenshot(browser: instance, comment: 'shortcut_after')
await_empty_ajax_queue(params)
end
=begin
@ -1588,6 +1597,7 @@ wait untill text in selector disabppears
log('window_keys', params)
instance = params[:browser] || @browser
instance.action.send_keys(params[:value]).perform
await_empty_ajax_queue(params)
end
=begin
@ -2122,19 +2132,14 @@ wait untill text in selector disabppears
mute_log: true,
)
found = false
7.times do
element = instance.find_elements(css: '.content.active .newTicket')[0]
if element
found = true
break
end
sleep 1
end
if !found
screenshot(browser: instance, comment: 'ticket_create_failed')
raise 'no ticket create screen found!'
end
watch_for(
browser: instance,
css: '.content.active .newTicket',
timeout: 30,
)
# Rumors say there is a modal reaper which will kill your modals if you dont sleep before a new ticket create
sleep 3
if data[:group]
if data[:group] == '-NONE-'
@ -2570,6 +2575,7 @@ wait untill text in selector disabppears
end
instance.find_elements(css: '.content.active .js-submit')[0].click
await_empty_ajax_queue(params)
# do not stay on tab
if params[:task_type] == 'closeTab' || params[:task_type] == 'closeNextInOverview'
@ -2867,7 +2873,7 @@ wait untill text in selector disabppears
instance = params[:browser] || @browser
instance.find_elements(css: '.js-overviewsMenuItem')[0].click
sleep 2
await_empty_ajax_queue(params)
execute(
browser: instance,
@ -2878,18 +2884,28 @@ wait untill text in selector disabppears
# js: '$(".content.active .overview-header").css("display", "none")',
#)
overviews = {}
instance.find_elements(css: '.content.active .sidebar a[href]').each do |element|
url = element.attribute('href')
url.gsub!(%r{(http|https)://.+?/(.+?)$}, '\\2')
overviews[url] = 0
#puts url.inspect
#puts element.inspect
end
overviews.each_key do |url|
count = instance.find_elements(css: ".content.active .sidebar a[href=\"#{url}\"] .badge")[0].text
overviews[url] = count.to_i
begin
overviews = {}
instance.find_elements(css: '.content.active .sidebar a[href]').each do |element|
url = element.attribute('href')
url.gsub!(%r{(http|https)://.+?/(.+?)$}, '\\2')
overviews[url] = 0
#puts url.inspect
#puts element.inspect
end
overviews.each_key do |url|
count = instance.find_elements(css: ".content.active .sidebar a[href=\"#{url}\"] .badge")[0].text
overviews[url] = count.to_i
end
rescue => e
retries ||= 0
retries += 1
sleep 0.5
retry if retries < 5
raise e
end
log('overview_counter', overviews)
overviews
end
@ -3340,6 +3356,7 @@ wait untill text in selector disabppears
element.send_keys(data[:name])
instance.find_elements(css: '.modal button.js-submit')[0].click
await_empty_ajax_queue(params)
modal_disappear(
browser: instance,
timeout: 5,
@ -3458,6 +3475,7 @@ wait untill text in selector disabppears
element.clear
element.send_keys(data[:first_response_time_in_text])
instance.find_elements(css: '.modal button.js-submit')[0].click
await_empty_ajax_queue(params)
modal_disappear(browser: instance)
7.times do
element = instance.find_elements(css: 'body')[0]
@ -3661,6 +3679,7 @@ wait untill text in selector disabppears
dropdown.select_by(:text, data[:signature])
end
instance.find_elements(css: '.modal button.js-submit')[0].click
await_empty_ajax_queue(params)
modal_disappear(browser: instance)
element = instance.find_elements(css: 'body')[0]
@ -3673,6 +3692,7 @@ wait untill text in selector disabppears
data[:member]&.each do |member|
instance.find_elements(css: 'a[href="#manage"]')[0].click
sleep 1
scroll_to(params.merge(css: '.content.active a[href="#manage/users"]'))
instance.find_elements(css: '.content.active a[href="#manage/users"]')[0].click
sleep 3
element = instance.find_elements(css: '.content.active [name="search"]')[0]
@ -3685,6 +3705,7 @@ wait untill text in selector disabppears
#instance.find_elements(:css => 'label:contains(" ' + action[:name] + '")')[0].click
instance.execute_script(%($(".js-groupList tr:contains(\\"#{data[:name]}\\") .js-groupListItem[value=#{member[:access]}]").prop("checked", true)))
instance.find_elements(css: '.modal button.js-submit')[0].click
await_empty_ajax_queue(params)
modal_disappear(browser: instance)
end
end
@ -3933,6 +3954,8 @@ wait untill text in selector disabppears
css: '.content.active a[href="#manage/roles"]',
mute_log: true,
)
await_text(container: '.content.active table tr td', text: data[:name])
instance.execute_script(%($('.content.active table tr td:contains(" #{data[:name]}")').first().click()))
modal_ready(browser: instance)
@ -4830,4 +4853,124 @@ wait untill text in selector disabppears
screenshot(browser: instance, comment: "object_manager_attribute_#{action}_failed")
raise "object_manager_attribute_#{action}_failed"
end
def check_alert(params = {})
instance = params[:browser] || @browser
tries = 5
begin
alert = instance.switch_to.alert
alert.dismiss()
rescue e
tries -= 1
sleep 0.5
retry if tries.positive?
raise e
end
end
=begin
This function waits for ajax requests and object form flow to be done
await_empty_ajax_queue
=end
def await_empty_ajax_queue(params = {})
return if params[:ajax] == false
instance = params[:browser] || @browser
10.times do
sleep 0.5
break if instance.execute_script('return typeof(App) === "undefined"')
break if instance.execute_script('return App.Ajax.queue().length').zero?
end
end
=begin
This function waits for a text to be ready in the dom. By default it searches in the active content.
await_text(text: 'New Ticket')
await_text(text: 'New Ticket', container: 'body')
=end
def await_text(params)
return if params[:ajax] == false
instance = params[:browser] || @browser
container = '.content.active'
if params[:container]
container = params[:container]
end
20.times do
log('await_text', params)
break if instance.execute_script("return $(\"#{container}:contains('#{params[:text]}')\").length").positive?
sleep 0.5
end
end
=begin
This function waits for the overview_counter to return a specific result.
await_overview_counter(view: '#ticket/view/all_unassigned', count: overview_counter_before['#ticket/view/all_unassigned'] - 2)
=end
def await_overview_counter(params)
result = nil
40.times do
result = overview_counter
if result[ params[:view] ] != params[:count]
sleep 0.5
next
end
break
end
assert_equal(params[:count], result[ params[:view] ])
end
=begin
This function waits for a search result to be available in the global search.
It can help to verify if a user is indexed in elastic search.
await_global_search(query: 'customer 1 firstname')
=end
def await_global_search(params)
instance = params[:browser] || @browser
30.times do
log('await_global_search', params)
set(
css: 'input#global-search',
value: params[:query],
)
break if instance.execute_script("return $(\"ul.global-search-result:visible:contains('#{params[:query]}')\").length") == 1
sleep 0.5
end
set(
css: 'input#global-search',
value: '',
)
end
end