search_spec.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Search', type: :system, authenticated: true, searchindex: true do
  4. let(:group_1) { create :group }
  5. let(:group_2) { create :group }
  6. let(:macro_without_group) { create :macro }
  7. let(:macro_note) { create :macro, name: 'Macro note', perform: { 'article.note'=>{ 'body' => 'macro body', 'internal' => 'true', 'subject' => 'macro note' } } }
  8. let(:macro_group1) { create :macro, groups: [group_1] }
  9. let(:macro_group2) { create :macro, groups: [group_2] }
  10. let(:ticket_1) { create :ticket, title: 'Testing Ticket 1', group: group_1 }
  11. let(:ticket_2) { create :ticket, title: 'Testing Ticket 2', group: group_2 }
  12. let(:note) { 'Test note' }
  13. before do
  14. ticket_1 && ticket_2
  15. searchindex_model_reload([::Ticket, ::Organization, ::User])
  16. end
  17. it 'shows default widgets' do
  18. fill_in id: 'global-search', with: '"Welcome"'
  19. click_on 'Show Search Details'
  20. within '#navigation .tasks a[data-key=Search]' do
  21. expect(page).to have_content '"Welcome"'
  22. end
  23. end
  24. context 'with ticket search result', authenticated_as: :authenticate do
  25. let(:agent) { create(:agent, groups: Group.all) }
  26. def authenticate
  27. ticket_1 && ticket_2
  28. agent
  29. end
  30. before do
  31. fill_in id: 'global-search', with: 'Testing'
  32. click_on 'Show Search Details'
  33. find('[data-tab-content=Ticket]').click
  34. end
  35. context 'checkbox' do
  36. it 'has checkbox for each ticket records' do
  37. within '.detail-search table.table' do
  38. expect(page).to have_xpath(".//td[contains(@class, 'js-checkbox-field')]//input[@type='checkbox']", visible: :all, minimum: 2)
  39. end
  40. end
  41. it 'has select all checkbox' do
  42. within '.detail-search table.table' do
  43. expect(page).to have_xpath(".//th//input[@type='checkbox' and @name='bulk_all']", visible: :all, count: 1)
  44. end
  45. end
  46. it 'shows bulkform when checkbox is checked' do
  47. within '.detail-search table.table' do
  48. find("tr[data-id='#{ticket_1.id}']").check('bulk', allow_label_click: true)
  49. end
  50. expect(page).to have_selector('.bulkAction.no-sidebar')
  51. expect(page).to have_no_selector('.bulkAction.no-sidebar.hide', visible: :all)
  52. end
  53. it 'shows bulkform when all checkbox is checked' do
  54. within '.detail-search table.table' do
  55. find('th.table-checkbox').check('bulk_all', allow_label_click: true)
  56. end
  57. expect(page).to have_selector('.bulkAction.no-sidebar')
  58. expect(page).to have_no_selector('.bulkAction.no-sidebar.hide', visible: :all)
  59. end
  60. it 'hides bulkform when checkbox is unchecked' do
  61. within '.detail-search table.table' do
  62. find('th.table-checkbox').check('bulk_all', allow_label_click: true)
  63. all('.js-tableBody tr.item').each { |row| row.uncheck('bulk', allow_label_click: true) }
  64. end
  65. expect(page).to have_selector('.bulkAction.no-sidebar.hide', visible: :hide)
  66. end
  67. end
  68. context 'with bulkform activated' do
  69. before do
  70. find('th.table-checkbox').check('bulk_all', allow_label_click: true)
  71. end
  72. it 'has group label' do
  73. within '.bulkAction .bulkAction-form' do
  74. expect(page).to have_content 'GROUP'
  75. end
  76. end
  77. it 'has owner label' do
  78. within '.bulkAction .bulkAction-form' do
  79. expect(page).to have_content 'OWNER'
  80. end
  81. end
  82. it 'has state label' do
  83. within '.bulkAction .bulkAction-form' do
  84. expect(page).to have_content 'STATE'
  85. end
  86. end
  87. it 'has priority label' do
  88. within '.bulkAction .bulkAction-form' do
  89. expect(page).to have_content 'PRIORITY'
  90. end
  91. end
  92. end
  93. context 'bulk note' do
  94. it 'adds note to selected ticket' do
  95. within :active_content do
  96. find("tr[data-id='#{ticket_1.id}']").check('bulk', allow_label_click: true)
  97. click '.js-confirm'
  98. find('.js-confirm-step textarea').fill_in with: note
  99. click '.js-submit'
  100. end
  101. expect do
  102. wait.until { ticket_1.articles.last&.body == note }
  103. end.not_to raise_error
  104. end
  105. end
  106. context 'with drag and drop' do
  107. context 'when checked tickets are dragged' do
  108. it 'shows the batch actions' do
  109. within(:active_content, '.main .table') do
  110. # get element to move
  111. element = page.find(:table_row, ticket_1.id).native
  112. click_and_hold(element)
  113. # move element a bit to display batch actions
  114. move_mouse_by(0, 5)
  115. # move mouse again to trigger the event for chrome
  116. move_mouse_by(0, 7)
  117. end
  118. expect(page).to have_selector('.batch-overlay-circle--top.js-batch-macro-circle')
  119. .and(have_selector('.batch-overlay-circle--bottom.js-batch-assign-circle'))
  120. end
  121. end
  122. end
  123. end
  124. context 'with ticket search result for macros bulk action', authenticated_as: :authenticate do
  125. let(:group_3) { create :group }
  126. let(:search_query) { 'Testing' }
  127. let(:ticket_3) { create :ticket, title: 'Testing Ticket 3', group: group_3 }
  128. let(:agent) { create(:agent, groups: Group.all) }
  129. before do
  130. fill_in id: 'global-search', with: search_query
  131. click_on 'Show Search Details'
  132. find('[data-tab-content=Ticket]').click
  133. end
  134. describe 'group-dependent macros' do
  135. def authenticate
  136. ticket_1 && ticket_2 && ticket_3
  137. macro_without_group && macro_group1 && macro_group2
  138. agent
  139. end
  140. it 'shows only non-group macro when ticket does not match any group macros' do
  141. within(:active_content) do
  142. display_macro_batches ticket_3
  143. expect(page).to have_selector(:macro_batch, macro_without_group.id)
  144. .and(have_no_selector(:macro_batch, macro_group1.id))
  145. .and(have_no_selector(:macro_batch, macro_group2.id))
  146. end
  147. end
  148. it 'shows non-group and matching group macros for matching ticket' do
  149. within(:active_content) do
  150. display_macro_batches ticket_1
  151. expect(page).to have_selector(:macro_batch, macro_without_group.id)
  152. .and(have_selector(:macro_batch, macro_group1.id))
  153. .and(have_no_selector(:macro_batch, macro_group2.id))
  154. end
  155. end
  156. end
  157. context 'with macro batch overlay' do
  158. shared_examples "adding 'small' class to macro element" do
  159. it 'adds a "small" class to the macro element' do
  160. within(:active_content) do
  161. display_macro_batches ticket_1
  162. expect(page).to have_selector('.batch-overlay-macro-entry.small')
  163. end
  164. end
  165. end
  166. shared_examples "not adding 'small' class to macro element" do
  167. it 'does not add a "small" class to the macro element' do
  168. within(:active_content) do
  169. display_macro_batches ticket_1
  170. expect(page).to have_no_selector('.batch-overlay-macro-entry.small')
  171. end
  172. end
  173. end
  174. shared_examples 'showing all macros' do
  175. it 'shows all macros' do
  176. within(:active_content) do
  177. display_macro_batches ticket_1
  178. expect(page).to have_selector('.batch-overlay-macro-entry', count: all)
  179. end
  180. end
  181. end
  182. shared_examples 'showing some macros' do |count|
  183. it 'shows all macros' do
  184. within(:active_content) do
  185. display_macro_batches ticket_1
  186. expect(page).to have_selector('.batch-overlay-macro-entry', count: count)
  187. end
  188. end
  189. end
  190. def authenticate
  191. ticket_1 && ticket_2
  192. Macro.destroy_all && (create_list :macro, all)
  193. agent
  194. end
  195. context 'with few macros' do
  196. let(:all) { 15 }
  197. context 'when on large screen', screen_size: :desktop do
  198. it_behaves_like 'showing all macros'
  199. it_behaves_like "not adding 'small' class to macro element"
  200. end
  201. context 'when on small screen', screen_size: :tablet do
  202. it_behaves_like 'showing all macros'
  203. it_behaves_like "not adding 'small' class to macro element"
  204. end
  205. end
  206. context 'with many macros' do
  207. let(:all) { 50 }
  208. context 'when on large screen', screen_size: :desktop do
  209. it_behaves_like 'showing some macros', 32
  210. end
  211. context 'when on small screen', screen_size: :tablet do
  212. it_behaves_like 'showing some macros', 24
  213. it_behaves_like "adding 'small' class to macro element"
  214. end
  215. end
  216. end
  217. end
  218. context 'Organization members', authenticated_as: :authenticate do
  219. let(:organization) { create(:organization) }
  220. let(:members) { organization.members.order(id: :asc) }
  221. def authenticate
  222. create_list(:customer, 50, organization: organization)
  223. true
  224. end
  225. before do
  226. fill_in id: 'global-search', with: organization.name.to_s
  227. end
  228. it 'shows only first 10 members' do
  229. expect(page).to have_text(organization.name)
  230. popover_on_hover(first('a.nav-tab.organization'))
  231. expect(page).to have_text(members[9].fullname, wait: 30)
  232. expect(page).to have_no_text(members[10].fullname)
  233. end
  234. end
  235. context 'inactive user and organizations' do
  236. before do
  237. create(:organization, name: 'Example Inc.', active: true)
  238. create(:organization, name: 'Example Inactive Inc.', active: false)
  239. create(:customer, firstname: 'Firstname', lastname: 'Active', active: true)
  240. create(:customer, firstname: 'Firstname', lastname: 'Inactive', active: false)
  241. searchindex_model_reload([::User, ::Organization])
  242. end
  243. it 'check that inactive organizations are marked correctly' do
  244. fill_in id: 'global-search', with: '"Example"'
  245. expect(page).to have_css('.nav-tab--search.organization', minimum: 2)
  246. expect(page).to have_css('.nav-tab--search.organization.is-inactive', count: 1)
  247. end
  248. it 'check that inactive users are marked correctly' do
  249. fill_in id: 'global-search', with: '"Firstname"'
  250. expect(page).to have_css('.nav-tab--search.user', minimum: 2)
  251. expect(page).to have_css('.nav-tab--search.user.is-inactive', count: 1)
  252. end
  253. it 'check that inactive users are also marked in the popover for the quick search result' do
  254. fill_in id: 'global-search', with: '"Firstname"'
  255. popover_on_hover(find('.nav-tab--search.user.is-inactive'))
  256. expect(page).to have_css('.popover-title .is-inactive', count: 1)
  257. end
  258. end
  259. describe 'Search is not triggered/updated if url of search is updated new search item or new search is triggered via global search #3873', authenticated_as: :authenticate do
  260. let(:agent) { create(:agent, groups: Group.all) }
  261. def authenticate
  262. ticket_1 && ticket_2
  263. agent
  264. end
  265. context 'when search changed via input box' do
  266. before do
  267. visit '#search'
  268. end
  269. it 'does switch search results properly' do
  270. page.find('.js-search').fill_in(with: '"Testing Ticket 1"', fill_options: { clear: :backspace })
  271. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 1')
  272. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 2')
  273. expect(current_url).to include('Testing%20Ticket%201')
  274. # switch by global search
  275. page.find('.js-search').fill_in(with: '"Testing Ticket 2"', fill_options: { clear: :backspace })
  276. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 2')
  277. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 1')
  278. expect(current_url).to include('Testing%20Ticket%202')
  279. end
  280. end
  281. context 'when search changed via global search' do
  282. before do
  283. fill_in id: 'global-search', with: '"Testing Ticket 1"'
  284. click_on 'Show Search Details'
  285. end
  286. it 'does switch search results properly' do
  287. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 1')
  288. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 2')
  289. expect(current_url).to include('Testing%20Ticket%201')
  290. # switch by global search
  291. fill_in id: 'global-search', with: '"Testing Ticket 2"'
  292. click_on 'Show Search Details'
  293. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 2')
  294. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 1')
  295. expect(current_url).to include('Testing%20Ticket%202')
  296. end
  297. end
  298. context 'when search is changed via url' do
  299. before do
  300. visit '#search/"Testing Ticket 1"'
  301. end
  302. it 'does switch search results properly' do
  303. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 1')
  304. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 2')
  305. expect(current_url).to include('Testing%20Ticket%201')
  306. # switch by url
  307. visit '#search/"Testing Ticket 2"'
  308. expect(page.find('.js-tableBody')).to have_text('Testing Ticket 2')
  309. expect(page.find('.js-tableBody')).to have_no_text('Testing Ticket 1')
  310. expect(current_url).to include('Testing%20Ticket%202')
  311. end
  312. end
  313. end
  314. context 'Assign user to multiple organizations #1573', authenticated_as: :authenticate do
  315. let(:organizations) { create_list(:organization, 20) }
  316. let(:customer) { create(:customer, organization: organizations[0], organizations: organizations[1..]) }
  317. context 'when agent' do
  318. def authenticate
  319. customer
  320. true
  321. end
  322. before do
  323. fill_in id: 'global-search', with: customer.firstname.to_s
  324. end
  325. it 'shows only first 3 organizations' do
  326. expect(page).to have_text(customer.firstname)
  327. popover_on_hover(first('a.nav-tab.user'))
  328. within '.popover' do
  329. expect(page).to have_text(organizations[2].name, wait: 30)
  330. expect(page).to have_no_text(organizations[10].name)
  331. end
  332. end
  333. end
  334. context 'when customer', authenticated_as: :customer do
  335. before do
  336. fill_in id: 'global-search', with: organizations[0].name.to_s
  337. end
  338. it 'does not show any organizations in global search because only agents have access to it' do
  339. within '.global-search-result' do
  340. expect(page).to have_no_text(organizations[0].name)
  341. end
  342. end
  343. end
  344. end
  345. describe 'Searches display all groups and owners on bulk selections #4054', authenticated_as: :authenticate do
  346. let(:group_1) { create(:group) }
  347. let(:group_2) { create(:group) }
  348. let(:agent_1) { create(:agent, groups: [group_1]) }
  349. let(:agent_2) { create(:agent, groups: [group_2]) }
  350. let(:agent_all) { create(:agent, groups: [group_1, group_2]) }
  351. let(:ticket_1) { create(:ticket, group: group_1, title: '4054 group 1') }
  352. let(:ticket_2) { create(:ticket, group: group_2, title: '4054 group 2') }
  353. def authenticate
  354. agent_1 && agent_2 && agent_all
  355. ticket_1 && ticket_2
  356. agent_all
  357. end
  358. def check_owner_empty
  359. expect(page).to have_select('owner_id', text: '-', visible: :all)
  360. expect(page).to have_no_select('owner_id', text: agent_1.fullname, visible: :all)
  361. expect(page).to have_no_select('owner_id', text: agent_2.fullname, visible: :all)
  362. end
  363. def click_ticket(ticket)
  364. page.find(".js-tableBody tr.item[data-id='#{ticket.id}'] td.js-checkbox-field").click
  365. end
  366. def check_owner_agent1_shown
  367. expect(page).to have_select('owner_id', text: agent_1.fullname)
  368. expect(page).to have_no_select('owner_id', text: agent_2.fullname)
  369. end
  370. def check_owner_agent2_shown
  371. expect(page).to have_no_select('owner_id', text: agent_1.fullname)
  372. expect(page).to have_select('owner_id', text: agent_2.fullname)
  373. end
  374. def check_owner_field
  375. check_owner_empty
  376. click_ticket(ticket_1)
  377. check_owner_agent1_shown
  378. click_ticket(ticket_1)
  379. click_ticket(ticket_2)
  380. check_owner_agent2_shown
  381. end
  382. context 'when search is used' do
  383. before do
  384. visit '#search/4054'
  385. end
  386. it 'does show the correct owner selection for each bulk action' do
  387. check_owner_field
  388. end
  389. end
  390. context 'when ticket overview is used' do
  391. before do
  392. visit '#ticket/view/all_unassigned'
  393. end
  394. it 'does show the correct owner selection for each bulk action' do
  395. check_owner_field
  396. end
  397. end
  398. end
  399. end