ticket_update_spec.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'system/apps/mobile_old/examples/core_workflow_examples'
  4. RSpec.describe 'Mobile > Ticket > Update', app: :mobile, authenticated_as: :agent, type: :system do
  5. let(:group) { Group.find_by(name: 'Users') }
  6. let(:group_2) { create(:group, name: 'Group 2') }
  7. let(:owner) { User.find_by(email: 'agent1@example.com') }
  8. let(:agent) { create(:agent, groups: [group, group_2]) }
  9. let(:ticket) { create(:ticket, title: 'Ticket Title', owner: owner, group: group) }
  10. def find_outer(label)
  11. find('.formkit-outer', text: label)
  12. end
  13. def find_by_label(label)
  14. state_selector = find_outer(label)
  15. state_selector.find('output', visible: :all)
  16. end
  17. def select_option(output, option)
  18. output.click
  19. click('[role="option"]', text: option)
  20. end
  21. def submit_form
  22. find_button('Save').click
  23. wait_for_gql('shared/entities/ticket/graphql/mutations/update.graphql')
  24. end
  25. # - policy says that only agent with "read" access, but not "change" access can view this
  26. # - if user can view ticket, he can also change it
  27. context 'when user cannot update ticket', authenticated_as: :viewer do
  28. let(:organization) { create(:organization, shared: true) }
  29. let(:viewer) do
  30. user = create(:agent, groups: [group], organization: organization)
  31. user.group_names_access_map = {
  32. group.name => 'read',
  33. }
  34. user.save!
  35. user
  36. end
  37. let(:owner) { create(:customer, groups: [group], organization: organization) }
  38. let(:ticket) { create(:ticket, owner: owner, group: group, organization: organization) }
  39. let(:tags) do
  40. [
  41. Tag::Item.lookup_by_name_and_create('foo'),
  42. Tag::Item.lookup_by_name_and_create('bar'),
  43. ]
  44. end
  45. before do
  46. create(:tag, o: ticket, tag_item: tags.first)
  47. create(:tag, o: ticket, tag_item: tags.last)
  48. end
  49. it 'does not show "save" button, but shows form as menu with sections' do
  50. visit "/tickets/#{ticket.id}/information"
  51. expect(page).to have_no_button('Save')
  52. expect(page).to have_no_css('output', text: 'Tags')
  53. expect(find('section', text: %r{Tags})).to have_text('foo, bar')
  54. expect(find('section', text: %r{Group})).to have_text(ticket.group.name)
  55. expect(find('section', text: %r{State})).to have_text(ticket.state.name)
  56. end
  57. end
  58. context 'when user can update ticket' do
  59. context 'when there are no custom object attributes' do
  60. it 'can edit ticket in ideal scenario without object attributes' do
  61. visit "/tickets/#{ticket.id}/information"
  62. wait_for_form_to_settle('form-ticket-edit')
  63. expect(page).to have_no_selector(:button, 'Save')
  64. within_form(form_updater_gql_number: 1) do
  65. title = find_input('Ticket title')
  66. expect(title).to have_value('Ticket Title')
  67. title.type('New Title')
  68. state = find_select('State')
  69. expect(state).to have_selected_option(ticket.state.name)
  70. state.select_option('closed')
  71. expect(state).to have_selected_option('closed')
  72. priority = find_select('Priority')
  73. expect(priority).to have_selected_option(ticket.priority.name)
  74. priority.select_option('3 high')
  75. expect(priority).to have_selected_option('3 high')
  76. end
  77. ticket.reload
  78. expect(ticket.title).not_to eq('New Title')
  79. expect(ticket.state.name).not_to eq('closed')
  80. expect(ticket.priority.name).not_to eq('3 high')
  81. submit_form
  82. ticket.reload
  83. expect(ticket.title).to eq('New Title')
  84. expect(ticket.state.name).to eq('closed')
  85. expect(ticket.priority.name).to eq('3 high')
  86. end
  87. it 'can reset the owner' do
  88. visit "/tickets/#{ticket.id}/information"
  89. wait_for_form_to_settle('form-ticket-edit')
  90. within_form(form_updater_gql_number: 1) do
  91. owner_field = find_select('Owner')
  92. expect(owner_field).to have_selected_option(owner.fullname)
  93. owner_field.clear_selection
  94. expect(owner_field).to have_no_selected_option(owner.fullname)
  95. end
  96. submit_form
  97. ticket.reload
  98. expect(ticket.owner.id).to be(1)
  99. end
  100. it 'changing ticket state to pending requires pending time', time_zone: 'Europe/London' do
  101. visit "/tickets/#{ticket.id}/information"
  102. wait_for_form_to_settle('form-ticket-edit')
  103. expect(page).to have_no_css('label', text: 'Pending until')
  104. date = 1.day.from_now.beginning_of_minute
  105. within_form(form_updater_gql_number: 1) do
  106. find_select('State').select_option('pending reminder')
  107. date_input = find_datepicker('Pending till')
  108. expect(date_input.input_element.value).to eq('')
  109. expect(page).to have_css('[role="status"][aria-label="Validation failed"]')
  110. date_input.type_datetime(date)
  111. end
  112. ticket.reload
  113. expect(ticket.pending_time).to be_nil
  114. submit_form
  115. ticket.reload
  116. expect(ticket.pending_time.localtime).to eq(date)
  117. end
  118. it 'can save form on another page' do
  119. visit "/tickets/#{ticket.id}/information"
  120. wait_for_form_to_settle('form-ticket-edit')
  121. within_form(form_updater_gql_number: 1) do
  122. find_input('Ticket title').type('New Title')
  123. end
  124. click('button', text: 'Customer')
  125. submit_form
  126. ticket.reload
  127. expect(ticket.title).to eq('New Title')
  128. end
  129. end
  130. context 'when changing group' do
  131. it 'owner depends on group' do
  132. visit "/tickets/#{ticket.id}/information"
  133. wait_for_form_to_settle('form-ticket-edit')
  134. within_form(form_updater_gql_number: 1) do
  135. owner_field = find_select('Owner')
  136. expect(owner_field).to have_selected_option(owner.fullname)
  137. group = find_select('Group')
  138. group.clear_selection
  139. expect(owner_field).to have_no_selected_option(owner.fullname)
  140. group.select_option('Users')
  141. owner_field.select_option(agent.fullname)
  142. expect(owner_field).to have_selected_option(agent.fullname)
  143. end
  144. ticket.reload
  145. expect(ticket.owner.id).not_to eq(agent.id)
  146. submit_form
  147. ticket.reload
  148. expect(ticket.owner.id).to eq(agent.id)
  149. end
  150. end
  151. context 'when there are custom object attributes' do
  152. it 'if attribute is required, block submit button, when value is empty', db_strategy: :reset do
  153. screens = { edit: { 'ticket.agent': { shown: true, required: true } } }
  154. attribute = create_attribute(
  155. :object_manager_attribute_text,
  156. object_name: 'Ticket',
  157. display: 'Custom Text',
  158. screens: screens
  159. )
  160. ticket[attribute.name] = 'Attribute Text'
  161. ticket.save!
  162. visit "/tickets/#{ticket.id}/information"
  163. wait_for_form_to_settle('form-ticket-edit')
  164. within_form(form_updater_gql_number: 1) do
  165. attribute_field = find_input(attribute.display)
  166. expect(attribute_field).to have_value('Attribute Text')
  167. attribute_field.clear
  168. expect(page).to have_css('[role="status"][aria-label="Validation failed"]')
  169. attribute_field.type('New Text')
  170. expect(page).to have_no_css('[role="status"][aria-label="Validation failed"]')
  171. expect(ticket[attribute.name]).to eq('Attribute Text')
  172. end
  173. submit_form
  174. ticket.reload
  175. expect(ticket[attribute.name]).to eq('New Text')
  176. end
  177. it 'can clear the value in select fields', db_strategy: :reset do
  178. screens = { edit: { 'ticket.agent': { shown: true, required: false } } }
  179. attribute = create_attribute(
  180. :object_manager_attribute_select,
  181. object_name: 'Ticket',
  182. display: 'Custom Text',
  183. screens: screens,
  184. data_option: {
  185. options: {
  186. 'name 1': 'name 1',
  187. 'name 2': 'name 2',
  188. },
  189. default: '',
  190. null: false,
  191. relation: '',
  192. maxlength: 255,
  193. nulloption: true,
  194. }
  195. )
  196. ObjectManager::Attribute.migration_execute
  197. ticket[attribute.name] = 'name 1'
  198. ticket.save!
  199. visit "/tickets/#{ticket.id}/information"
  200. wait_for_form_to_settle('form-ticket-edit')
  201. within_form(form_updater_gql_number: 1) do
  202. attribute_field = find_select(attribute.display)
  203. expect(attribute_field).to have_value('name 1')
  204. attribute_field.clear_selection
  205. end
  206. submit_form
  207. ticket.reload
  208. expect(ticket[attribute.name]).to be_nil
  209. end
  210. end
  211. context 'with customer user', authenticated_as: :customer do
  212. let(:customer) { create(:customer) }
  213. let(:ticket) { create(:ticket, title: 'Ticket Title', customer: customer, state_name: state_name) }
  214. before do
  215. visit "/tickets/#{ticket.id}/information"
  216. wait_for_form_to_settle('form-ticket-edit')
  217. end
  218. context 'with the default create state (new)' do
  219. let(:state_name) { 'new' }
  220. it 'does not apply default follow-up state' do
  221. expect(find_select('State')).to have_selected_option('new')
  222. find_button('Go back').click
  223. find_button('Add reply').click
  224. within_form(form_updater_gql_number: 1) do
  225. find_editor('Text').type('Foobar')
  226. end
  227. find_button('Done').click
  228. wait_for_form_updater 3
  229. find_link('Ticket Title').click
  230. expect(find_select('State')).to have_selected_option('new')
  231. end
  232. end
  233. context 'with a different state (pending reminder)' do
  234. let(:state_name) { 'pending reminder' }
  235. it 'applies default follow-up state' do
  236. expect(find_select('State')).to have_selected_option('pending reminder')
  237. find_button('Go back').click
  238. find_button('Add reply').click
  239. within_form(form_updater_gql_number: 1) do
  240. find_editor('Text').type('Foobar')
  241. end
  242. find_button('Done').click
  243. wait_for_form_updater 3
  244. find_link('Ticket Title').click
  245. expect(find_select('State')).to have_selected_option('open')
  246. find_button('Go back').click
  247. find_button('Edit reply').click
  248. # Discard article body to trigger the reset of the state.
  249. find_button('Discard your unsaved changes').click
  250. find_button('Discard article').click
  251. wait_for_form_updater 4
  252. find_link('Ticket Title').click
  253. state = find_select('State')
  254. expect(state).to have_selected_option('pending reminder')
  255. expect(state.has_css?('[data-dirty="true"]', wait: false)).to be(false)
  256. end
  257. end
  258. end
  259. end
  260. describe 'Core Workflow' do
  261. include_examples 'mobile app: core workflow' do
  262. let(:object_name) { 'Ticket' }
  263. let(:form_updater_gql_number) { 1 }
  264. let(:before_it) do
  265. lambda {
  266. visit "/tickets/#{ticket.id}/information"
  267. wait_for_form_to_settle('form-ticket-edit')
  268. }
  269. end
  270. end
  271. end
  272. end