view_spec.rb 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Ticket views', authenticated_as: :authenticate, type: :system do
  4. def authenticate
  5. true
  6. end
  7. context 'macros' do
  8. let(:group1) { create(:group) }
  9. let(:group2) { create(:group) }
  10. let(:macro_without_group) { create(:macro) }
  11. let(:macro_note) { create(:macro, perform: { 'article.note'=>{ 'body' => 'macro body', 'internal' => 'true', 'subject' => 'macro note' } }) }
  12. let(:macro_group1) { create(:macro, groups: [group1]) }
  13. let(:macro_group2) { create(:macro, groups: [group2]) }
  14. let(:ticket1) { create(:ticket, group: group1) }
  15. let(:ticket2) { create(:ticket, group: group2) }
  16. describe 'group-dependent macros' do
  17. let(:agent) { create(:agent, groups: Group.all) }
  18. def authenticate
  19. ticket1 && ticket2
  20. macro_without_group && macro_group1 && macro_group2
  21. agent
  22. end
  23. it 'shows only non-group macro when ticket does not match any group macros' do
  24. visit '#ticket/view/all_open'
  25. within(:active_content) do
  26. display_macro_batches Ticket.first
  27. expect(page).to have_selector(:macro_batch, macro_without_group.id)
  28. .and(have_no_selector(:macro_batch, macro_group1.id))
  29. .and(have_no_selector(:macro_batch, macro_group2.id))
  30. end
  31. end
  32. it 'shows non-group and matching group macros for matching ticket' do
  33. visit '#ticket/view/all_open'
  34. within(:active_content) do
  35. display_macro_batches ticket1
  36. expect(page).to have_selector(:macro_batch, macro_without_group.id)
  37. .and(have_selector(:macro_batch, macro_group1.id))
  38. .and(have_no_selector(:macro_batch, macro_group2.id))
  39. end
  40. end
  41. end
  42. describe 'macro article creation' do
  43. def authenticate
  44. macro_note
  45. true
  46. end
  47. it 'can use macro to create article' do
  48. visit '#ticket/view/all_open'
  49. within(:active_content) do
  50. display_macro_batches Ticket.first
  51. move_mouse_to find(:macro_batch, macro_note.id)
  52. release_mouse
  53. expect do
  54. wait.until { Ticket.first.articles.last.subject == 'macro note' }
  55. end.not_to raise_error
  56. end
  57. end
  58. end
  59. context 'when saving is blocked by one of selected tickets' do
  60. let(:ticket) { Ticket.first }
  61. let(:core_workflow_action) { { 'ticket.priority_id': { operator: 'remove_option', remove_option: '3' } } }
  62. let(:core_workflow) { create(:core_workflow, :active_and_screen, :perform_action) }
  63. let(:macro_perform) do
  64. {
  65. 'ticket.priority_id': { pre_condition: 'specific', value: 3.to_s }
  66. }
  67. end
  68. let(:macro_priority) { create(:macro, perform: macro_perform) }
  69. def authenticate
  70. core_workflow && macro_priority && ticket1
  71. true
  72. end
  73. it 'shows modal with blocking ticket title' do
  74. visit '#ticket/view/all_open'
  75. within(:active_content) do
  76. display_macro_batches ticket
  77. move_mouse_to find(:macro_batch, macro_priority.id)
  78. release_mouse
  79. in_modal do
  80. expect(page).to have_text(ticket.title)
  81. end
  82. end
  83. end
  84. end
  85. describe 'when agent cannot change some of the tickets' do
  86. let(:agent) { create(:agent) }
  87. def authenticate
  88. macro_without_group
  89. agent
  90. .tap { |user| user.user_groups.create! group: ticket1.group, access: 'full' }
  91. .tap { |user| user.user_groups.create! group: ticket2.group, access: 'overview' }
  92. end
  93. before do
  94. visit '#ticket/view/all_open'
  95. end
  96. it 'show macros if agent cannot change selected tickets' do
  97. display_macro_batches ticket1
  98. within(:active_content) do
  99. expect(page).to have_no_text(%r{No macros available}i)
  100. .and(have_selector(:macro_batch, macro_without_group.id))
  101. end
  102. end
  103. it 'show no macros if agent cannot change selected tickets' do
  104. display_macro_batches ticket2
  105. within(:active_content) do
  106. expect(page).to have_text(%r{No macros available}i)
  107. .and(have_text(%r{no change permission}i))
  108. .and(have_no_selector(:macro_batch, macro_without_group.id))
  109. end
  110. end
  111. end
  112. describe 'when user is agent-customer' do
  113. let(:agent_customer) { create(:agent_and_customer) }
  114. def authenticate
  115. ticket1.update!(customer: agent_customer)
  116. ticket2
  117. macro_without_group && macro_group1 && macro_group2
  118. agent_customer
  119. .tap { |user| user.groups << ticket2.group }
  120. end
  121. before do
  122. visit '#ticket/view/all_open'
  123. end
  124. it 'show no macros if the ticket is customer-like' do
  125. display_macro_batches ticket1
  126. within :active_content do
  127. expect(page).to have_text(%r{No macros available}i)
  128. .and(have_text(%r{no change permission}i))
  129. .and(have_no_selector(:macro_batch, macro_without_group.id))
  130. .and(have_no_selector(:macro_batch, macro_group1.id))
  131. .and(have_no_selector(:macro_batch, macro_group2.id))
  132. end
  133. end
  134. it 'show macros if tickets are only agent-like' do
  135. display_macro_batches ticket2
  136. within :active_content do
  137. expect(page).to have_no_text(%r{No macros available}i)
  138. .and(have_selector(:macro_batch, macro_without_group.id))
  139. .and(have_no_selector(:macro_batch, macro_group1.id))
  140. .and(have_selector(:macro_batch, macro_group2.id))
  141. end
  142. end
  143. end
  144. describe 'when user is customer' do
  145. let(:customer) { create(:customer) }
  146. def authenticate
  147. ticket1.update!(customer: customer)
  148. customer
  149. end
  150. before do
  151. visit '#ticket/view/my_tickets'
  152. end
  153. it 'shows no overlay' do
  154. display_macro_batches ticket1
  155. within :active_content do
  156. expect(page).to have_no_selector('.batch-overlay-backdrop')
  157. end
  158. end
  159. end
  160. context 'with macro batch overlay' do
  161. shared_examples "adding 'small' class to macro element" do
  162. it 'adds a "small" class to the macro element' do
  163. within(:active_content) do
  164. display_macro_batches Ticket.first
  165. expect(page).to have_selector('.batch-overlay-macro-entry.small')
  166. end
  167. end
  168. end
  169. shared_examples "not adding 'small' class to macro element" do
  170. it 'does not add a "small" class to the macro element' do
  171. within(:active_content) do
  172. display_macro_batches Ticket.first
  173. expect(page).to have_no_selector('.batch-overlay-macro-entry.small')
  174. end
  175. end
  176. end
  177. shared_examples 'showing all macros' do
  178. it 'shows all macros' do
  179. within(:active_content) do
  180. display_macro_batches Ticket.first
  181. expect(page).to have_selector('.batch-overlay-macro-entry', count: all)
  182. end
  183. end
  184. end
  185. shared_examples 'showing some macros' do |count|
  186. it 'shows all macros' do
  187. within(:active_content) do
  188. display_macro_batches Ticket.first
  189. expect(page).to have_selector('.batch-overlay-macro-entry', count: count)
  190. end
  191. end
  192. end
  193. def authenticate
  194. Macro.destroy_all && create_list(:macro, all)
  195. true
  196. end
  197. before do
  198. visit '#ticket/view/all_open'
  199. end
  200. context 'with few macros' do
  201. let(:all) { 15 }
  202. context 'when on large screen', screen_size: :desktop do
  203. it_behaves_like 'showing all macros'
  204. it_behaves_like "not adding 'small' class to macro element"
  205. end
  206. context 'when on small screen', screen_size: :tablet do
  207. it_behaves_like 'showing all macros'
  208. it_behaves_like "not adding 'small' class to macro element"
  209. end
  210. end
  211. context 'with many macros' do
  212. let(:all) { 50 }
  213. context 'when on large screen', screen_size: :desktop do
  214. it_behaves_like 'showing some macros', 32
  215. end
  216. context 'when on small screen', screen_size: :tablet do
  217. it_behaves_like 'showing some macros', 24
  218. it_behaves_like "adding 'small' class to macro element"
  219. end
  220. end
  221. end
  222. end
  223. context 'when performing a Bulk action' do
  224. context 'when creating a Note', authenticated_as: :user do
  225. let(:group) { create(:group) }
  226. let(:user) { create(:admin, groups: [group]) }
  227. let(:ticket1) { create(:ticket, state_name: 'open', owner: user, group: group) }
  228. let(:ticket2) { create(:ticket, state_name: 'open', owner: user, group: group) }
  229. let(:note) { Faker::Lorem.sentence }
  230. it 'adds note to all selected tickets' do
  231. ticket1 && ticket2
  232. visit 'ticket/view/my_assigned'
  233. within :active_content do
  234. all('.js-checkbox-field', count: 2).each(&:click)
  235. click '.js-confirm'
  236. find('.js-confirm-step textarea').fill_in with: note
  237. click '.js-submit'
  238. end
  239. expect do
  240. wait.until { [ ticket1.articles.last&.body, ticket2.articles.last&.body ] == [note, note] }
  241. end.not_to raise_error
  242. end
  243. end
  244. # https://github.com/zammad/zammad/issues/3568
  245. # We need a manual ticket creation to test the correct behaviour of the bulk functionality, because of some
  246. # leftovers after the creation in the the javascript assets store.
  247. context 'when performed a manual Ticket creation', authenticated_as: :agent do
  248. let(:customer) { create(:customer) }
  249. let(:group) { Group.find_by(name: 'Users') }
  250. let(:agent) { create(:agent, groups: [group]) }
  251. let!(:template) { create(:template, :dummy_data, group: group, owner: agent, customer: customer) }
  252. before do
  253. visit 'ticket/create'
  254. within(:active_content) do
  255. use_template(template)
  256. click('.js-submit')
  257. find('.ticket-article-item')
  258. end
  259. end
  260. it 'check that no duplicated article was created after usage of bulk action' do
  261. click('.menu-item[href="#ticket/view"]')
  262. created_ticket_id = Ticket.last.id
  263. within(:active_content) do
  264. click("tr[data-id='#{created_ticket_id}'] .js-checkbox-field")
  265. find('select[name="priority_id"] option[value="1"]').select_option
  266. click('.js-confirm')
  267. click('.js-submit')
  268. await_empty_ajax_queue
  269. # Check if still only one article exists on the ticket.
  270. click("tr[data-id='#{created_ticket_id}'] a")
  271. expect(page).to have_css('.ticket-article-item', count: 1)
  272. end
  273. end
  274. end
  275. context 'when saving is blocked by one of selected tickets', authenticated_as: :pre_authentication do
  276. let(:core_workflow) { create(:core_workflow, :active_and_screen, :perform_action) }
  277. let(:ticket1) { create(:ticket, group: Group.first) }
  278. def pre_authentication
  279. core_workflow && ticket1
  280. true
  281. end
  282. it 'shows modal with blocking ticket title' do
  283. visit 'ticket/view/all_open'
  284. within(:active_content) do
  285. find("tr[data-id='#{ticket1.id}']").check('bulk', allow_label_click: true)
  286. select '3 high', from: 'priority_id'
  287. click '.js-confirm'
  288. click '.js-submit'
  289. in_modal do
  290. expect(page).to have_text(ticket1.title)
  291. end
  292. end
  293. end
  294. end
  295. end
  296. context 'Setting "ui_table_group_by_show_count"', authenticated_as: :authenticate, db_strategy: :reset do
  297. let(:custom_attribute) { create(:object_manager_attribute_select, name: 'grouptest') }
  298. let(:tickets) do
  299. [
  300. create(:ticket, group: Group.find_by(name: 'Users')),
  301. create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_1'),
  302. create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_2'),
  303. create(:ticket, group: Group.find_by(name: 'Users'), grouptest: 'key_1')
  304. ]
  305. end
  306. def authenticate
  307. custom_attribute
  308. ObjectManager::Attribute.migration_execute
  309. tickets
  310. Overview.find_by(name: 'Open Tickets').update(group_by: custom_attribute.name)
  311. Setting.set('ui_table_group_by_show_count', true)
  312. true
  313. end
  314. it 'shows correct ticket counts' do
  315. visit 'ticket/view/all_open'
  316. within(:active_content) do
  317. expect(page).to have_css('.js-tableBody td b', text: '(1)')
  318. .and(have_css('.js-tableBody td b', text: 'value_1 (2)'))
  319. .and(have_css('.js-tableBody td b', text: 'value_2 (1)'))
  320. end
  321. end
  322. end
  323. context 'Customer', authenticated_as: :authenticate do
  324. let(:customer) { create(:customer, :with_org) }
  325. let(:ticket) { create(:ticket, customer: customer) }
  326. def authenticate
  327. ticket
  328. customer
  329. end
  330. it 'shows ticket in my tickets' do
  331. visit 'ticket/view/my_tickets'
  332. expect(page).to have_text(ticket.title)
  333. end
  334. it 'shows ticket in my organization tickets' do
  335. visit 'ticket/view/my_tickets'
  336. click_on 'My Organization Tickets'
  337. expect(page).to have_text(ticket.title)
  338. end
  339. end
  340. describe 'Grouping by custom attribute', authenticated_as: :authenticate, db_strategy: :reset do
  341. def authenticate
  342. custom_attribute
  343. ObjectManager::Attribute.migration_execute
  344. tickets
  345. Overview.find_by(link: 'all_unassigned').update(group_by: custom_attribute.name)
  346. true
  347. end
  348. context 'when sorted by custom object date' do
  349. let(:custom_attribute) { create(:object_manager_attribute_date, name: 'cdate') }
  350. let(:tickets) do
  351. [
  352. create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2021-08-18'),
  353. create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2019-01-19'),
  354. create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2018-01-17'),
  355. create(:ticket, group: Group.find_by(name: 'Users'), cdate: '2018-08-19')
  356. ]
  357. end
  358. it 'does show the values grouped and sorted by date key value (yyy-mm-dd) instead of display value' do
  359. visit 'ticket/view/all_unassigned'
  360. headers = all('.js-tableBody td[colspan="6"]').map(&:text)
  361. expect(headers).to eq ['01/17/2018', '08/19/2018', '01/19/2019', '08/18/2021', '-']
  362. end
  363. end
  364. context 'when sorted by custom object select', authenticated_as: :authenticate, db_strategy: :reset do
  365. let(:custom_attribute) do
  366. create(:object_manager_attribute_select,
  367. name: 'cselect',
  368. data_option_options: {
  369. 'a' => 'Zzz a',
  370. 'b' => 'Yyy b',
  371. 'c' => 'Xxx c',
  372. })
  373. end
  374. let(:tickets) do
  375. [
  376. create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'a'),
  377. create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'b'),
  378. create(:ticket, group: Group.find_by(name: 'Users'), cselect: 'c')
  379. ]
  380. end
  381. it 'does show the values grouped and sorted by display value instead of key value' do
  382. visit 'ticket/view/all_unassigned'
  383. headers = all('.js-tableBody td[colspan="6"]').map(&:text)
  384. expect(headers).to eq ['-', 'Xxx c', 'Yyy b', 'Zzz a']
  385. end
  386. end
  387. end
  388. end