checklist_spec.rb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Ticket zoom > Checklist', authenticated_as: :authenticate, type: :system do
  4. let(:action_user) { create(:agent, groups: Group.all) }
  5. let(:other_agent) { create(:agent, groups: Group.all) }
  6. let(:ticket) { create(:ticket, group: Group.first) }
  7. def authenticate
  8. Setting.set('checklist', true)
  9. action_user
  10. end
  11. def perform_item_action(id, action)
  12. page.find(".checklistShow tr[data-id='#{id}'] .js-action", wait: 0).click
  13. page.find(".checklistShow tr[data-id='#{id}'] li[data-table-action='#{action}']", wait: 0).click
  14. rescue => e
  15. retry_click ||= 5
  16. retry_click -= 1
  17. sleep 1
  18. raise e if retry_click < 1
  19. retry
  20. end
  21. def perform_checklist_action(text)
  22. click '.sidebar[data-tab=checklist] .js-actions'
  23. click_on text
  24. rescue => e
  25. retry_click ||= 5
  26. retry_click -= 1
  27. sleep 1
  28. raise e if retry_click < 1
  29. retry
  30. end
  31. before do
  32. visit "#ticket/zoom/#{ticket.id}"
  33. end
  34. it 'does show the sidebar for the checklists' do
  35. expect(page).to have_css('.tabsSidebar-tab[data-tab=checklist]')
  36. Setting.set('checklist', false)
  37. expect(page).to have_no_css('.tabsSidebar-tab[data-tab=checklist]')
  38. end
  39. it 'does create a checklist' do
  40. click '.tabsSidebar-tab[data-tab=checklist]'
  41. expect(page).to have_button('Add empty checklist')
  42. click_on('Add empty checklist')
  43. expect(page).to have_no_button('Add empty checklist')
  44. wait.until { Checklist.where(ticket: ticket).present? }
  45. end
  46. it 'does show handle subscriptions for badge when sidebar is not opened' do
  47. create(:checklist, ticket: ticket)
  48. expect(page).to have_css(".tabsSidebar-tab[data-tab='checklist'] .js-tabCounter", text: ticket.checklist.items.count)
  49. end
  50. context 'when checklist exists' do
  51. let(:checklist) { create(:checklist, ticket: ticket) }
  52. let(:item) { checklist.items.last }
  53. before do
  54. checklist
  55. click '.tabsSidebar-tab[data-tab=checklist]'
  56. wait.until { page.text.include?(checklist.name) }
  57. await_empty_ajax_queue
  58. end
  59. it 'does show handle subscriptions' do
  60. item.update(text: SecureRandom.uuid)
  61. expect(page).to have_text(item.text)
  62. item.destroy
  63. expect(page).to have_no_text(item.text)
  64. checklist.destroy
  65. expect(page).to have_button('Add empty checklist')
  66. end
  67. it 'does remove the checklist' do
  68. perform_checklist_action('Remove checklist')
  69. click_on 'delete'
  70. expect(page).to have_text('Add empty checklist')
  71. end
  72. it 'does rename the checklist' do
  73. perform_checklist_action('Rename checklist')
  74. checklist_name = SecureRandom.uuid
  75. find('#checklistTitleEditText').fill_in with: checklist_name, fill_options: { clear: :backspace }
  76. page.find('.js-confirm').click
  77. wait.until { checklist.reload.name == checklist_name }
  78. end
  79. it 'does add item' do
  80. find('.checklistShowButtons .js-add').click
  81. wait.until { checklist.items.last.text == '' }
  82. end
  83. it 'does check item' do
  84. perform_item_action(item.id, 'check')
  85. wait.until { item.reload.checked == true }
  86. end
  87. it 'does uncheck item' do
  88. item.update(checked: true)
  89. perform_item_action(item.id, 'uncheck')
  90. wait.until { item.reload.checked == false }
  91. end
  92. it 'does edit item' do
  93. perform_item_action(item.id, 'edit')
  94. item_text = SecureRandom.uuid
  95. find(".checklistShow tr[data-id='#{item.id}'] .js-input").fill_in with: item_text, fill_options: { clear: :backspace }
  96. page.find('.js-confirm').click
  97. wait.until { item.reload.text == item_text }
  98. end
  99. it 'does edit item with a ticket link' do
  100. perform_item_action(item.id, 'edit')
  101. item_text = "Ticket##{Ticket.first.number}"
  102. find(".checklistShow tr[data-id='#{item.id}'] .js-input").fill_in with: item_text, fill_options: { clear: :backspace }
  103. page.find('.js-confirm').click
  104. expect(page).to have_link(Ticket.first.title)
  105. end
  106. it 'does reorder item' do
  107. click_on 'Reorder'
  108. first_item = checklist.items.first
  109. last_item = checklist.items.last
  110. element = page.find(".checklistShow tr[data-id='#{first_item.id}'] .draggable")
  111. element.drag_to(page.find(".checklistShow tr[data-id='#{last_item.id}'] .draggable"))
  112. click_on 'Save'
  113. wait.until { page.text.index(first_item.text) > page.text.index(last_item.text) }
  114. wait.until { checklist.reload.sorted_item_ids.last.to_s == first_item.id.to_s }
  115. end
  116. it 'does not abort edit when subscription is updating but including it afterwards' do
  117. perform_item_action(item.id, 'edit')
  118. item_text = SecureRandom.uuid
  119. find(".checklistShow tr[data-id='#{item.id}'] .js-input").fill_in with: item_text, fill_options: { clear: :backspace }
  120. # simulate other users change
  121. other_item_text = SecureRandom.uuid
  122. checklist.items.create!(text: other_item_text, created_by: other_agent, updated_by: other_agent)
  123. # not really another way to be absolutely sure that this works
  124. sleep 5
  125. # the new item will be synced after saving
  126. expect(page).to have_no_text(other_item_text)
  127. # it's important that the old edit mode does not abort
  128. page.find('.js-confirm').click
  129. # then both items arrive in the UI
  130. expect(page).to have_text(item_text)
  131. expect(page).to have_text(other_item_text)
  132. end
  133. it 'does delete item' do
  134. perform_item_action(item.id, 'delete')
  135. click_on 'delete'
  136. wait.until { Checklist::Item.find_by(id: item.id).blank? }
  137. end
  138. context 'with links' do
  139. let(:checklist) do
  140. checklist = create(:checklist, ticket: ticket)
  141. checklist.items.last.update(text: 'http://google.de test')
  142. checklist
  143. end
  144. it 'does edit item with link' do
  145. expect(page).to have_link('google.de')
  146. perform_item_action(item.id, 'edit')
  147. item_text = SecureRandom.uuid
  148. find(".checklistShow tr[data-id='#{item.id}'] .js-input").fill_in with: item_text, fill_options: { clear: :backspace }
  149. page.find('.js-confirm').click
  150. wait.until { item.reload.text == item_text }
  151. end
  152. end
  153. context 'with ticket links' do
  154. context 'with access' do
  155. let(:ticket_link) { create(:ticket, title: SecureRandom.uuid, group: Group.first) }
  156. let(:checklist) do
  157. checklist = create(:checklist, ticket: ticket)
  158. checklist.items.last.update(text: "Ticket##{ticket_link.number}")
  159. checklist
  160. end
  161. it 'does show link to the ticket' do
  162. expect(page).to have_link(ticket_link.title)
  163. end
  164. end
  165. context 'without access' do
  166. let(:ticket_link) { create(:ticket, title: SecureRandom.uuid) }
  167. let(:checklist) do
  168. checklist = create(:checklist, ticket: ticket)
  169. checklist.items.last.update(text: "Ticket##{ticket_link.number}")
  170. checklist
  171. end
  172. it 'does show the not authorized for the item' do
  173. expect(page).to have_text('Not authorized')
  174. end
  175. end
  176. end
  177. end
  178. context 'when using a checklist template' do
  179. let(:checklist_template) { create(:checklist_template) }
  180. before do
  181. checklist_template
  182. click '.tabsSidebar-tab[data-tab=checklist]'
  183. wait.until { page.find('[name="checklist_template_id"]') }
  184. await_empty_ajax_queue
  185. end
  186. it 'does add checklist from template' do
  187. expect(page).to have_button('Add from a template')
  188. expect(page).to have_select('checklist_template_id')
  189. # Sometimes, by clicking the button, nothing happens.
  190. sleep 0.1
  191. click_on('Add from a template')
  192. wait.until { page.has_content?('Please select a checklist template.') }
  193. select checklist_template.name, from: 'checklist_template_id'
  194. wait.until { page.has_no_content?('Please select a checklist template.') }
  195. click_on('Add from a template')
  196. wait.until { Checklist.where(ticket: ticket).present? }
  197. expect(Checklist.where(ticket: ticket).last.items.count).to eq(checklist_template.items.count)
  198. checklist_template.items.each do |item|
  199. expect(page).to have_text(item.text)
  200. end
  201. end
  202. end
  203. context 'when checklist modal on submit' do
  204. let(:checklist) { create(:checklist, ticket: ticket, name: SecureRandom.uuid) }
  205. def authenticate
  206. pre_auth
  207. true
  208. end
  209. context 'when activated and set' do
  210. let(:pre_auth) do
  211. Setting.set('checklist', true)
  212. checklist
  213. end
  214. it 'does show modal' do
  215. select 'closed', from: 'State'
  216. click '.js-submit'
  217. expect(page).to have_text('You have unchecked items in the checklist')
  218. end
  219. it 'does switch to sidebar' do
  220. select 'closed', from: 'State'
  221. click '.js-submit'
  222. page.find('.modal-footer .js-submit').click
  223. expect(page).to have_text(checklist.name.upcase)
  224. end
  225. context 'when ticket is closed' do
  226. let(:pre_auth) do
  227. Setting.set('checklist', true)
  228. checklist
  229. other_agent
  230. ticket.update(state: Ticket::State.find_by(name: 'closed'))
  231. end
  232. it 'does not show modal' do
  233. select other_agent.fullname, from: 'Owner'
  234. click '.js-submit'
  235. wait.until { ticket.reload.owner.fullname == other_agent.fullname }
  236. expect(page).to have_no_text('You have unchecked items in the checklist')
  237. end
  238. end
  239. context 'when time accounting is also activated' do
  240. let(:pre_auth) do
  241. Setting.set('checklist', true)
  242. Setting.set('time_accounting', true)
  243. checklist
  244. end
  245. it 'does show both modals' do
  246. find('.articleNewEdit-body').send_keys('Forwarding with the attachment')
  247. select 'closed', from: 'State'
  248. click '.js-submit'
  249. expect(page.find('.modal-body')).to have_text('You have unchecked items in the checklist')
  250. page.find('.modal-footer .js-skip').click
  251. expect(page.find('.modal-body')).to have_text('Accounted Time'.upcase)
  252. page.find('.modal-footer .js-skip').click
  253. wait.until { ticket.reload.state.name == 'closed' }
  254. end
  255. end
  256. end
  257. context 'when deactivated and set' do
  258. let(:pre_auth) do
  259. Setting.set('checklist', false)
  260. checklist
  261. end
  262. it 'does not show modal' do
  263. select 'closed', from: 'State'
  264. click '.js-submit'
  265. wait.until { ticket.reload.state.name == 'closed' }
  266. expect(page).to have_no_text('You have unchecked items in the checklist')
  267. end
  268. end
  269. context 'when activated and completed' do
  270. let(:pre_auth) do
  271. Setting.set('checklist', true)
  272. checklist.items.map { |item| item.update(checked: true) }
  273. end
  274. it 'does not show modal' do
  275. select 'closed', from: 'State'
  276. click '.js-submit'
  277. wait.until { ticket.reload.state.name == 'closed' }
  278. expect(page).to have_no_text('You have unchecked items in the checklist')
  279. end
  280. end
  281. context 'when activated and no checklist' do
  282. let(:pre_auth) do
  283. Setting.set('checklist', true)
  284. end
  285. it 'does not show modal' do
  286. select 'closed', from: 'State'
  287. click '.js-submit'
  288. wait.until { ticket.reload.state.name == 'closed' }
  289. expect(page).to have_no_text('You have unchecked items in the checklist')
  290. end
  291. end
  292. end
  293. describe 'Checklist badge counter does not update when linked tickets change state. #5319' do
  294. let(:ticket) do
  295. ticket = create(:ticket, group: Group.first)
  296. checklist = create(:checklist, ticket: ticket)
  297. checklist.items.last.update(text: "Ticket##{ticket_link.number}")
  298. ticket
  299. end
  300. let(:ticket_link) { create(:ticket, group: Group.first) }
  301. it 'does update for badge when sidebar is not opened and same user updates related tickets' do
  302. ticket_link.update!(state: Ticket::State.find_by(name: 'closed'), updated_by: action_user)
  303. expect(page).to have_css(".tabsSidebar-tab[data-tab='checklist'] .js-tabCounter", text: ticket.checklist.incomplete)
  304. end
  305. end
  306. end