checklist_spec.rb 13 KB

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