create_spec.rb 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'system/examples/core_workflow_examples'
  4. require 'system/examples/text_modules_examples'
  5. RSpec.describe 'Ticket Create', type: :system do
  6. context 'when calling without session' do
  7. describe 'redirect to' do
  8. it 'login screen after certain create was called', authenticated_as: false do
  9. visit '#ticket/create/id/1234'
  10. expect(page).to have_css('#login')
  11. end
  12. it 'login screen after generic create was called', authenticated_as: false do
  13. visit '#ticket/create'
  14. expect(page).to have_css('#login')
  15. end
  16. end
  17. end
  18. context 'when logged in as non admin' do
  19. let(:agent) { create(:agent) }
  20. it 'show manage templates text only', authenticated_as: :agent do
  21. visit 'ticket/create'
  22. expect(page).to have_no_css('div.js-createLink', visible: :all)
  23. expect(page).to have_no_css('div.js-createTextOnly', visible: :hidden)
  24. end
  25. end
  26. context 'when using the sidebar' do
  27. context 'when using a template' do
  28. before do
  29. visit 'ticket/create'
  30. use_template(create(:template, :dummy_data, customer: create(:customer, :with_org)))
  31. end
  32. it 'does show the edit link for the customer' do
  33. click '.tabsSidebar-tab[data-tab=customer]'
  34. click '#userAction'
  35. click_on 'Edit Customer'
  36. modal_ready
  37. end
  38. it 'does show the edit link for the organization' do
  39. click '.tabsSidebar-tab[data-tab=organization]'
  40. click '#userAction'
  41. click_on 'Edit Organization'
  42. modal_ready
  43. end
  44. end
  45. %w[idoit gitlab github].each do |service_name|
  46. it "#{service_name} tab is hidden" do
  47. visit 'ticket/create'
  48. expect(page).to have_no_css(".tabsSidebar-tab[data-tab=#{service_name}]")
  49. end
  50. context "when #{service_name} is enabled" do
  51. before do
  52. Setting.set("#{service_name}_integration", true)
  53. end
  54. context 'when agent' do
  55. it "#{service_name} tab is visible" do
  56. visit 'ticket/create'
  57. expect(page).to have_css(".tabsSidebar-tab[data-tab=#{service_name}]")
  58. end
  59. end
  60. context 'when customer', authenticated_as: :customer do
  61. let(:customer) { create(:customer) }
  62. it "#{service_name} tab is hidden" do
  63. visit 'customer_ticket_new'
  64. expect(page).to have_no_css(".tabsSidebar-tab[data-tab=#{service_name}]")
  65. end
  66. end
  67. end
  68. end
  69. end
  70. context 'when logged in as admin' do
  71. let(:admin) { create(:admin) }
  72. it 'show manage templates link', authenticated_as: :admin do
  73. visit 'ticket/create'
  74. expect(page).to have_no_css('div.js-createLink', visible: :hidden)
  75. expect(page).to have_no_css('div.js-createTextOnly', visible: :all)
  76. end
  77. end
  78. context 'when applying ticket templates' do
  79. let(:agent) { create(:agent, groups: [permitted_group]) }
  80. let(:permitted_group) { create(:group) }
  81. let(:unpermitted_group) { create(:group) }
  82. let!(:template_unpermitted_group) { create(:template, :dummy_data, group: unpermitted_group, owner: agent) }
  83. let!(:template_permitted_group) { create(:template, :dummy_data, group: permitted_group, owner: agent) }
  84. before { visit 'ticket/create' }
  85. # Regression test for issue #2424 - Unavailable ticket template attributes get applied
  86. it 'unavailable attributes do not get applied', authenticated_as: :agent do
  87. use_template(template_unpermitted_group)
  88. expect(all('[name="group_id"] option', visible: :all).map(&:value)).not_to eq unpermitted_group.id.to_s
  89. expect(find('[name="owner_id"]', visible: :all).value).to eq agent.id.to_s
  90. end
  91. it 'available attributes get applied', authenticated_as: :agent do
  92. use_template(template_permitted_group)
  93. expect(find('[name="group_id"]', visible: :all).value).to eq permitted_group.id.to_s
  94. expect(find('[name="owner_id"]', visible: :all).value).to eq agent.id.to_s
  95. end
  96. context 'with tag values' do
  97. let(:template) do
  98. create(:template,
  99. options: {
  100. 'ticket.tags': {
  101. value: template_value,
  102. operator: operator,
  103. }
  104. })
  105. end
  106. shared_examples 'merging with existing tags in a dirty form' do
  107. it 'merges with existing tags in a dirty form' do
  108. set_tokens_field_value('tags', %w[baz qux foo])
  109. use_template(template)
  110. check_tokens_field_value('tags', ['baz', 'qux', *template_value.split(', ')])
  111. end
  112. end
  113. shared_examples 'replacing tags in a clean form' do
  114. it 'replaces tags in a clean form' do
  115. use_template(template)
  116. check_tokens_field_value('tags', template_value.split(', '), visible: :all)
  117. end
  118. end
  119. shared_examples 'leaving tags empty in a clean form' do
  120. it 'does nothing in a clean form' do
  121. use_template(template)
  122. check_tokens_field_value('tags', '')
  123. end
  124. end
  125. context 'with add operator' do
  126. let(:operator) { 'add' }
  127. let(:template_value) { 'foo, bar' }
  128. it_behaves_like 'replacing tags in a clean form'
  129. it_behaves_like 'merging with existing tags in a dirty form'
  130. end
  131. context 'with remove operator' do
  132. let(:operator) { 'remove' }
  133. let(:template_value) { 'foo, bar' }
  134. it_behaves_like 'leaving tags empty in a clean form'
  135. it 'removes existing tags in a dirty form' do
  136. set_tokens_field_value('tags', %w[foo bar baz qux])
  137. use_template(template)
  138. check_tokens_field_value('tags', %w[baz qux])
  139. end
  140. end
  141. context 'without operator (legacy)' do
  142. let(:operator) { nil }
  143. let(:template_value) { 'foo, bar' }
  144. it_behaves_like 'replacing tags in a clean form'
  145. it_behaves_like 'merging with existing tags in a dirty form'
  146. end
  147. context 'with empty value' do
  148. let(:operator) { nil }
  149. let(:template_value) { nil }
  150. it_behaves_like 'leaving tags empty in a clean form'
  151. it 'leaves existing tags untouched in a dirty form' do
  152. set_tokens_field_value('tags', %w[baz qux])
  153. use_template(template)
  154. check_tokens_field_value('tags', %w[baz qux])
  155. end
  156. end
  157. end
  158. end
  159. context 'when using text modules' do
  160. include_examples 'text modules', path: 'ticket/create'
  161. end
  162. describe 'object manager attributes maxlength', authenticated_as: :authenticate, db_strategy: :reset do
  163. def authenticate
  164. create(:object_manager_attribute_text, :required_screen, name: 'maxtest', display: 'maxtest', data_option: {
  165. 'type' => 'text',
  166. 'maxlength' => 3,
  167. 'null' => true,
  168. 'translate' => false,
  169. 'default' => '',
  170. 'options' => {},
  171. 'relation' => '',
  172. })
  173. ObjectManager::Attribute.migration_execute
  174. true
  175. end
  176. it 'checks ticket create' do
  177. visit 'ticket/create'
  178. within(:active_content) do
  179. fill_in 'maxtest', with: 'hellu'
  180. expect(page.find_field('maxtest').value).to eq('hel')
  181. end
  182. end
  183. end
  184. describe 'object manager attributes default date', time_zone: 'Europe/London' do
  185. before :all do # rubocop:disable RSpec/BeforeAfterAll
  186. screens = {
  187. 'create_top' => {
  188. '-all-' => {
  189. 'null' => true
  190. }
  191. },
  192. }
  193. create(:object_manager_attribute_date, name: 'date_test', display: 'date_test', default: 24, screens: screens)
  194. create(:object_manager_attribute_datetime, name: 'datetime_test', display: 'datetime_test', default: 100, screens: screens)
  195. ObjectManager::Attribute.migration_execute # rubocop:disable Zammad/ExistsDbStrategy
  196. end
  197. after :all do # rubocop:disable RSpec/BeforeAfterAll
  198. ObjectManager::Attribute.where(name: %i[date_test datetime_test]).destroy_all
  199. end
  200. before do
  201. visit '/'
  202. template = create(:template, :dummy_data)
  203. travel 1.month
  204. browser_travel_to Time.current
  205. visit 'ticket/create'
  206. use_template template, without_taskbar: true
  207. end
  208. let(:field_date) { find 'input[name="{date}date_test"]', visible: :all }
  209. let(:field_time) { find 'input[name="{datetime}datetime_test"]', visible: :all }
  210. it 'prefills date' do
  211. expect(field_date.value).to eq 1.day.from_now.to_date.to_s
  212. end
  213. it 'prefills datetime' do
  214. expect(Time.zone.parse(field_time.value)).to eq 100.minutes.from_now.change(sec: 0, usec: 0)
  215. end
  216. it 'saves dates' do
  217. click '.js-submit'
  218. date = 1.day.from_now.to_date
  219. time = 100.minutes.from_now.change(sec: 0)
  220. expect(Ticket.last).to have_attributes date_test: date, datetime_test: time
  221. end
  222. it 'allows to save with different values' do
  223. date = 2.days.from_now.to_date
  224. time = 200.minutes.from_now.change(sec: 0)
  225. field_date.sibling('[data-item=date]').set date.strftime('%m/%d/%Y')
  226. field_time.sibling('[data-item=date]').set time.strftime('%m/%d/%Y')
  227. field_time.sibling('[data-item=time]').set time.strftime('%H:%M')
  228. click '.js-submit'
  229. expect(Ticket.last).to have_attributes date_test: date, datetime_test: time
  230. end
  231. it 'allows to save with cleared value' do
  232. field_date.sibling('[data-item=date]').click
  233. find('.datepicker .clear').click
  234. field_time.sibling('[data-item=date]').click
  235. find('.datepicker .clear').click
  236. click '.js-submit'
  237. expect(Ticket.last).to have_attributes date_test: nil, datetime_test: nil
  238. end
  239. end
  240. describe 'GitLab Integration', :integration, authenticated_as: :authenticate, required_envs: %w[GITLAB_ENDPOINT GITLAB_APITOKEN] do
  241. let(:customer) { create(:customer) }
  242. let(:agent) { create(:agent, groups: [Group.find_by(name: 'Users')]) }
  243. let!(:template) { create(:template, :dummy_data, group: Group.find_by(name: 'Users'), owner: agent, customer: customer) }
  244. def authenticate
  245. Setting.set('gitlab_integration', true)
  246. Setting.set('gitlab_config', {
  247. api_token: ENV['GITLAB_APITOKEN'],
  248. endpoint: ENV['GITLAB_ENDPOINT'],
  249. })
  250. true
  251. end
  252. it 'creates a ticket with links' do
  253. visit 'ticket/create'
  254. within(:active_content) do
  255. use_template(template)
  256. # switch to gitlab sidebar
  257. click('.tabsSidebar-tab[data-tab=gitlab]')
  258. click('.sidebar-header-headline.js-headline')
  259. # add issue
  260. click_on 'Link issue'
  261. fill_in 'link', with: ENV['GITLAB_ISSUE_LINK']
  262. click_on 'Submit'
  263. # verify issue
  264. content = find('.sidebar-git-issue-content')
  265. expect(content).to have_text('#1 Example issue')
  266. expect(content).to have_text('critical')
  267. expect(content).to have_text('special')
  268. expect(content).to have_text('important milestone')
  269. expect(content).to have_text('zammad-robot')
  270. # create Ticket
  271. click '.js-submit'
  272. # check stored data
  273. expect(Ticket.last.preferences[:gitlab][:issue_links][0]).to eq(ENV['GITLAB_ISSUE_LINK'])
  274. end
  275. end
  276. end
  277. describe 'GitHub Integration', :integration, authenticated_as: :authenticate, required_envs: %w[GITHUB_ENDPOINT GITHUB_APITOKEN] do
  278. let(:customer) { create(:customer) }
  279. let(:agent) { create(:agent, groups: [Group.find_by(name: 'Users')]) }
  280. let!(:template) { create(:template, :dummy_data, group: Group.find_by(name: 'Users'), owner: agent, customer: customer) }
  281. def authenticate
  282. Setting.set('github_integration', true)
  283. Setting.set('github_config', {
  284. api_token: ENV['GITHUB_APITOKEN'],
  285. endpoint: ENV['GITHUB_ENDPOINT'],
  286. })
  287. true
  288. end
  289. it 'creates a ticket with links' do
  290. visit 'ticket/create'
  291. within(:active_content) do
  292. use_template(template)
  293. # switch to github sidebar
  294. click('.tabsSidebar-tab[data-tab=github]')
  295. click('.sidebar-header-headline.js-headline')
  296. # add issue
  297. click_on 'Link issue'
  298. fill_in 'link', with: ENV['GITHUB_ISSUE_LINK']
  299. click_on 'Submit'
  300. # verify issue
  301. content = find('.sidebar-git-issue-content')
  302. expect(content).to have_text('#1575 GitHub integration')
  303. expect(content).to have_text('enhancement')
  304. expect(content).to have_text('integration')
  305. expect(content).to have_text('4.0')
  306. expect(content).to have_text('Thorsten')
  307. # create Ticket
  308. click '.js-submit'
  309. # check stored data
  310. expect(Ticket.last.preferences[:github][:issue_links][0]).to eq(ENV['GITHUB_ISSUE_LINK'])
  311. end
  312. end
  313. end
  314. describe 'Core Workflow' do
  315. include_examples 'core workflow' do
  316. let(:object_name) { 'Ticket' }
  317. let(:before_it) do
  318. lambda {
  319. ensure_websocket(check_if_pinged: false) do
  320. visit 'ticket/create'
  321. end
  322. }
  323. end
  324. end
  325. end
  326. # https://github.com/zammad/zammad/issues/2669
  327. context 'when canceling new ticket creation' do
  328. it 'closes the dialog' do
  329. visit 'ticket/create'
  330. task_key = find(:task_active)['data-key']
  331. expect { click('.js-cancel') }.to change { has_selector?(:task_with, task_key, wait: 0) }.to(false)
  332. end
  333. it 'asks for confirmation if the dialog was modified' do
  334. visit 'ticket/create'
  335. task_key = find(:task_active)['data-key']
  336. find('[name=title]').fill_in with: 'Title'
  337. click '.js-cancel'
  338. in_modal do
  339. click '.js-submit'
  340. end
  341. expect(page).to have_no_selector(:task_with, task_key)
  342. end
  343. it 'asks for confirmation if attachment was added' do
  344. visit 'ticket/create'
  345. within :active_content do
  346. page.find('input#fileUpload_1[data-initialized="true"]', visible: :all).set(Rails.root.join('test/data/mail/mail001.box'))
  347. await_empty_ajax_queue
  348. expect(page).to have_css('.attachments .attachment-delete', visible: :all)
  349. find('.js-cancel').click
  350. end
  351. in_modal do
  352. expect(page).to have_text 'Tab has changed'
  353. end
  354. end
  355. end
  356. context 'when uploading attachment' do
  357. it 'shows an error if server throws an error' do
  358. allow(Store).to receive(:create!) { raise 'Error' }
  359. visit 'ticket/create'
  360. within :active_content do
  361. page.find('input#fileUpload_1[data-initialized="true"]', visible: :all).set(Rails.root.join('test/data/mail/mail001.box'))
  362. await_empty_ajax_queue
  363. end
  364. in_modal do
  365. expect(page).to have_text 'Error'
  366. end
  367. end
  368. end
  369. context 'when closing taskbar tab for new ticket creation' do
  370. it 'close task bar entry after some changes in ticket create form' do
  371. visit 'ticket/create'
  372. within(:active_content) do
  373. find('[name=title]').fill_in with: 'Title'
  374. end
  375. wait.until { find(:task_active)['data-key'].present? }
  376. taskbar_tab_close(find(:task_active)['data-key'])
  377. end
  378. end
  379. describe 'customer selection to check the field search' do
  380. before do
  381. create(:customer, active: true)
  382. create(:customer, active: false)
  383. end
  384. it 'check for inactive customer in customer/organization selection' do
  385. visit 'ticket/create'
  386. within(:active_content) do
  387. find('[name=customer_id] ~ .user-select.token-input').fill_in with: '**'
  388. expect(page).to have_css('ul.recipientList > li.recipientList-entry', minimum: 2)
  389. expect(page).to have_css('ul.recipientList > li.recipientList-entry.is-inactive', count: 1)
  390. end
  391. end
  392. end
  393. context 'when agent and customer user login after another' do
  394. let(:agent) { create(:agent, password: 'test') }
  395. let(:customer) { create(:customer, password: 'test') }
  396. it 'customer user should not have agent object attributes', authenticated_as: false do
  397. # Create agent session and fetch object attributes.
  398. login(
  399. username: agent.login,
  400. password: 'test'
  401. )
  402. visit 'ticket/create'
  403. expect(page).to have_field('customer_id', type: 'hidden')
  404. # Remove local object attributes bound to the session.
  405. # There was an issue (#1856) where the old attribute values
  406. # persisted and were stored as the original attributes.
  407. logout
  408. # Create customer session and fetch object attributes.
  409. login(
  410. username: customer.login,
  411. password: 'test'
  412. )
  413. visit 'customer_ticket_new'
  414. expect(page).to have_no_field('customer_id', type: 'hidden')
  415. end
  416. end
  417. context 'when state options have a special translation', authenticated_as: :authenticate do
  418. let(:admin_de) { create(:admin, preferences: { locale: 'de-de' }) }
  419. context 'when translated state option has a single quote' do
  420. def authenticate
  421. open_tranlation = Translation.where(locale: 'de-de', source: 'open')
  422. open_tranlation.update(target: "off'en")
  423. admin_de
  424. end
  425. it 'shows the translated state options correctly' do
  426. visit 'ticket/create'
  427. expect(page).to have_select('state_id', with_options: ["off'en"])
  428. end
  429. end
  430. end
  431. describe 'It should be possible to show attributes which are configured shown false #3726', authenticated_as: :authenticate, db_strategy: :reset do
  432. let(:field_name) { SecureRandom.uuid }
  433. let(:field) do
  434. create(:object_manager_attribute_text, name: field_name, display: field_name, screens: {
  435. 'create_middle' => {
  436. 'ticket.agent' => {
  437. 'shown' => false,
  438. 'required' => false,
  439. }
  440. }
  441. })
  442. ObjectManager::Attribute.migration_execute
  443. end
  444. before do
  445. visit 'ticket/create'
  446. end
  447. context 'when field visible' do
  448. let(:workflow) do
  449. create(:core_workflow,
  450. object: 'Ticket',
  451. perform: { "ticket.#{field_name}" => { 'operator' => 'show', 'show' => 'true' } })
  452. end
  453. def authenticate
  454. field
  455. workflow
  456. true
  457. end
  458. it 'does show up the field' do
  459. expect(page).to have_css("div[data-attribute-name='#{field_name}']")
  460. end
  461. end
  462. context 'when field hidden' do
  463. def authenticate
  464. field
  465. true
  466. end
  467. it 'does not show the field' do
  468. expect(page).to have_css("div[data-attribute-name='#{field_name}'].is-hidden.is-removed", visible: :hidden)
  469. end
  470. end
  471. end
  472. describe 'Support workflow mechanism to do pending reminder state hide pending time use case #3790', authenticated_as: :authenticate do
  473. let(:template) { create(:template, :dummy_data) }
  474. def add_state
  475. Ticket::State.create_or_update(
  476. name: 'pending customer feedback',
  477. state_type: Ticket::StateType.find_by(name: 'pending reminder'),
  478. ignore_escalation: true,
  479. created_by_id: 1,
  480. updated_by_id: 1,
  481. )
  482. end
  483. def update_screens
  484. attribute = ObjectManager::Attribute.get(
  485. object: 'Ticket',
  486. name: 'state_id',
  487. )
  488. attribute.data_option[:filter] = Ticket::State.where(active: true).by_category_ids(:viewable)
  489. attribute.screens[:create_middle]['ticket.agent'][:filter] = Ticket::State.where(active: true).by_category_ids(:viewable_agent_new)
  490. attribute.screens[:create_middle]['ticket.customer'][:filter] = Ticket::State.where(active: true).by_category_ids(:viewable_customer_new)
  491. attribute.screens[:edit]['ticket.agent'][:filter] = Ticket::State.where(active: true).by_category_ids(:viewable_agent_edit)
  492. attribute.screens[:edit]['ticket.customer'][:filter] = Ticket::State.where(active: true).by_category_ids(:viewable_customer_edit)
  493. attribute.save!
  494. end
  495. def create_flow
  496. create(:core_workflow,
  497. object: 'Ticket',
  498. condition_selected: { 'ticket.state_id'=>{ 'operator' => 'is', 'value' => Ticket::State.find_by(name: 'pending customer feedback').id.to_s } },
  499. perform: { 'ticket.pending_time'=> { 'operator' => 'remove', 'remove' => 'true' } })
  500. end
  501. def authenticate
  502. add_state
  503. update_screens
  504. create_flow
  505. template
  506. true
  507. end
  508. before do
  509. visit 'ticket/create'
  510. use_template(template)
  511. end
  512. it 'does make it possible to create pending states where the pending time is optional and not visible' do
  513. select 'pending customer feedback', from: 'state_id'
  514. click '.js-submit'
  515. expect(current_url).to include('ticket/zoom')
  516. expect(Ticket.last.state_id).to eq(Ticket::State.find_by(name: 'pending customer feedback').id)
  517. expect(Ticket.last.pending_time).to be_nil
  518. end
  519. end
  520. context 'default priority', authenticated_as: :authenticate do
  521. let(:template) { create(:template, :dummy_data) }
  522. let(:ticket_priority) { create(:ticket_priority, default_create: true) }
  523. let(:another_priority) { Ticket::Priority.find(1) }
  524. let(:priority_field) { find('[name=priority_id]') }
  525. def authenticate
  526. template
  527. ticket_priority
  528. true
  529. end
  530. it 'shows default priority on load' do
  531. visit 'ticket/create'
  532. expect(priority_field.value).to eq ticket_priority.id.to_s
  533. end
  534. it 'does not reset to default priority on reload' do
  535. visit 'ticket/create'
  536. taskbar_timestamp = Taskbar.last.updated_at
  537. priority_field.select another_priority.name
  538. wait.until { Taskbar.last.updated_at != taskbar_timestamp }
  539. refresh
  540. expect(priority_field.reload.value).to eq another_priority.id.to_s
  541. end
  542. it 'saves default priority' do
  543. visit 'ticket/create'
  544. use_template template
  545. click '.js-submit'
  546. expect(Ticket.last).to have_attributes(priority: ticket_priority)
  547. end
  548. it 'saves different priority if overriden' do
  549. visit 'ticket/create'
  550. use_template template
  551. priority_field.select another_priority.name
  552. click '.js-submit'
  553. expect(Ticket.last).to have_attributes(priority: another_priority)
  554. end
  555. end
  556. describe 'When looking for customers, it is no longer possible to change into organizations #3815' do
  557. before do
  558. visit 'ticket/create'
  559. # modal reaper ;)
  560. sleep 3
  561. end
  562. context 'when less than 10 customers' do
  563. let(:organization) { Organization.first }
  564. it 'has no show more option' do
  565. find('[name=customer_id_completion]').fill_in with: 'zam'
  566. expect(page).to have_css("li.js-organization[data-organization-id='#{organization.id}']")
  567. page.find("li.js-organization[data-organization-id='#{organization.id}']").click
  568. expect(page).to have_css("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers.hidden", visible: :all)
  569. end
  570. end
  571. context 'when more than 10 customers', authenticated_as: :authenticate do
  572. def authenticate
  573. customers
  574. true
  575. end
  576. let(:organization) { create(:organization, name: 'Zammed') }
  577. let(:customers) do
  578. create_list(:customer, 50, organization: organization)
  579. end
  580. it 'does paginate through organization' do
  581. find('[name=customer_id_completion]').fill_in with: 'zam'
  582. expect(page).to have_css("li.js-organization[data-organization-id='#{organization.id}']")
  583. page.find("li.js-organization[data-organization-id='#{organization.id}']").click
  584. wait.until { page.all("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li", visible: :all).count == 12 } # 10 users + back + show more button
  585. expect(page).to have_css("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers[organization-member-limit='10']")
  586. scroll_into_view('li.js-showMoreMembers')
  587. page.find("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers").click
  588. wait.until { page.all("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li", visible: :all).count == 27 } # 25 users + back + show more button
  589. expect(page).to have_css("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers[organization-member-limit='25']")
  590. scroll_into_view('li.js-showMoreMembers')
  591. page.find("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers").click
  592. wait.until { page.all("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li", visible: :all).count == 52 } # 50 users + back + show more button
  593. scroll_into_view('li.js-showMoreMembers')
  594. expect(page).to have_css("ul.recipientList-organizationMembers[organization-id='#{organization.id}'] li.js-showMoreMembers.hidden", visible: :all)
  595. end
  596. end
  597. end
  598. describe 'Ticket create screen will loose attachments by time #3827' do
  599. before do
  600. visit 'ticket/create'
  601. end
  602. it 'does not loose attachments on rerender of the ui' do
  603. # upload two files
  604. page.find('input#fileUpload_1[data-initialized="true"]', visible: :all).set(Rails.root.join('test/data/mail/mail001.box'))
  605. await_empty_ajax_queue
  606. wait.until { page.all('div.attachment-delete.js-delete', visible: :all).count == 1 }
  607. expect(page).to have_text('mail001.box')
  608. page.find('input#fileUpload_1', visible: :all).set(Rails.root.join('test/data/mail/mail002.box'))
  609. await_empty_ajax_queue
  610. wait.until { page.all('div.attachment-delete.js-delete', visible: :all).count == 2 }
  611. expect(page).to have_text('mail002.box')
  612. # remove last file
  613. begin
  614. page.evaluate_script("$('div.attachment-delete.js-delete:last').trigger('click')") # not interactable
  615. rescue # Lint/SuppressedException
  616. # because its not interactable it also
  617. # returns this weird exception for the jquery
  618. # even tho it worked fine
  619. end
  620. await_empty_ajax_queue
  621. wait.until { page.all('div.attachment-delete.js-delete', visible: :all).count == 1 }
  622. expect(page).to have_text('mail001.box')
  623. expect(page).to have_no_text('mail002.box')
  624. # simulate rerender b
  625. page.evaluate_script("App.Event.trigger('ui:rerender')")
  626. expect(page).to have_text('mail001.box')
  627. expect(page).to have_no_text('mail002.box')
  628. end
  629. end
  630. describe 'Invalid group and owner list for tickets created via customer profile #3835' do
  631. let(:invalid_ticket) { create(:ticket) }
  632. before do
  633. visit "#ticket/create/id/#{invalid_ticket.id}/customer/#{User.find_by(firstname: 'Nicole').id}"
  634. end
  635. it 'does show an empty list of owners' do
  636. wait.until { page.all('select[name=owner_id] option').count == 1 }
  637. expect(page.all('select[name=owner_id] option').count).to eq(1)
  638. end
  639. end
  640. # https://github.com/zammad/zammad/issues/3825
  641. describe 'CC token field' do
  642. before do
  643. visit 'ticket/create'
  644. find('[data-type=email-out]').click
  645. end
  646. it 'can be cleared by cutting out text' do
  647. add_email 'asd@example.com'
  648. add_email 'def@example.com'
  649. find('.token', text: 'def@example.com').double_click
  650. send_keys([magic_key, 'x'])
  651. find('.token').click # trigger blur
  652. expect(find('[name="cc"]', visible: :all).value).to eq 'asd@example.com'
  653. end
  654. def add_email(input)
  655. field = find_field('CC')
  656. field.fill_in with: input
  657. field.execute_script "$(this).trigger('blur')" # trigger blur
  658. find '.token', text: input # wait for email to tokenize
  659. end
  660. end
  661. describe 'No signature on new ticket if email is default message type #3844', authenticated_as: :authenticate do
  662. def authenticate
  663. Setting.set('ui_ticket_create_default_type', 'email-out')
  664. Group.where.not(name: 'Users').each { |g| g.update(active: false) }
  665. true
  666. end
  667. before do
  668. visit 'ticket/create'
  669. end
  670. it 'does render the create screen with an initial core workflow state to set signatures and other defaults properly' do
  671. expect(page.find('.richtext-content')).to have_text('Support')
  672. end
  673. end
  674. describe 'Zammad 5 mail template double signature #3816', authenticated_as: :authenticate do
  675. let(:agent_template) { create(:agent) }
  676. let!(:template) do
  677. create(
  678. :template,
  679. :dummy_data,
  680. group: Group.first, owner: agent_template,
  681. body: 'Content dummy.<br><br><div data-signature="true" data-signature-id="1"> Test Other Agent<br><br>--<br> Super Support - Waterford Business Park<br> 5201 Blue Lagoon Drive - 8th Floor &amp; 9th Floor - Miami, 33126 USA<br> Email: hot@example.com - Web: <a href="http://www.example.com/" rel="nofollow noreferrer noopener" target="_blank">http://www.example.com/</a><br>--</div>'
  682. )
  683. end
  684. def authenticate
  685. Group.first.update(signature: Signature.first)
  686. true
  687. end
  688. before do
  689. visit 'ticket/create'
  690. find('[data-type=email-out]').click
  691. end
  692. it 'does not show double signature on template usage' do
  693. set_tree_select_value('group_id', Group.first.name)
  694. use_template(template)
  695. expect(page).to have_no_text('Test Other Agent')
  696. end
  697. end
  698. describe 'Tree select value cannot be set to "-" (empty) with Trigger/Scheduler/Core workflow #4024', authenticated_as: :authenticate, db_strategy: :reset do
  699. let(:field_name) { SecureRandom.uuid }
  700. let(:field) do
  701. create(:object_manager_attribute_tree_select, :required_screen, name: field_name, display: field_name)
  702. ObjectManager::Attribute.migration_execute
  703. end
  704. let(:workflow) do
  705. create(:core_workflow,
  706. object: 'Ticket',
  707. condition_selected: { 'ticket.priority_id'=>{ 'operator' => 'is', 'value' => Ticket::Priority.find_by(name: '3 high').id.to_s } },
  708. perform: { "ticket.#{field_name}" => { 'operator' => 'select', 'select' => 'Incident' } })
  709. end
  710. let(:workflow2) do
  711. create(:core_workflow,
  712. object: 'Ticket',
  713. condition_selected: { 'ticket.priority_id'=>{ 'operator' => 'is', 'value' => Ticket::Priority.find_by(name: '2 normal').id.to_s } },
  714. perform: { "ticket.#{field_name}" => { 'operator' => 'select', 'select' => '' } })
  715. end
  716. def authenticate
  717. field
  718. workflow
  719. workflow2
  720. true
  721. end
  722. before do
  723. visit 'ticket/create'
  724. end
  725. it 'does select the field value properly' do
  726. page.find('[name=priority_id]').select '3 high'
  727. wait.until { page.find("input[name='#{field_name}']", visible: :all).value == 'Incident' }
  728. page.find('[name=priority_id]').select '2 normal'
  729. wait.until { page.find("input[name='#{field_name}']", visible: :all).value == '' }
  730. end
  731. end
  732. describe 'Assign user to multiple organizations #1573' do
  733. let(:organization1) { create(:organization) }
  734. let(:organization2) { create(:organization) }
  735. let(:organization3) { create(:organization) }
  736. let(:organization4) { create(:organization) }
  737. let(:user1) { create(:agent, organization: organization1, organizations: [organization2, organization3]) }
  738. let(:user2) { create(:agent, organization: organization4) }
  739. let(:customer1) { create(:customer, organization: organization1, organizations: [organization2, organization3]) }
  740. let(:customer2) { create(:customer, organization: organization4) }
  741. context 'when agent', authenticated_as: :authenticate do
  742. def authenticate
  743. user1
  744. user2
  745. true
  746. end
  747. before do
  748. visit 'ticket/create'
  749. end
  750. it 'does not show the organization field for user 1' do
  751. find('[name=customer_id_completion]').fill_in with: user1.firstname
  752. find("li.recipientList-entry.js-object[data-object-id='#{user1.id}']").click
  753. expect(page).to have_css("div[data-attribute-name='organization_id']")
  754. end
  755. it 'does show the organization field for user 2' do
  756. find('[name=customer_id_completion]').fill_in with: user2.firstname
  757. find("li.recipientList-entry.js-object[data-object-id='#{user2.id}']").click
  758. expect(page).to have_no_css("div[data-attribute-name='organization_id']")
  759. end
  760. it 'can create tickets for secondary organizations' do
  761. fill_in 'Title', with: 'test'
  762. find('.richtext-content').send_keys 'test'
  763. set_tree_select_value('group_id', Group.first.name)
  764. find('[name=customer_id_completion]').fill_in with: user1.firstname
  765. wait.until { page.all("li.recipientList-entry.js-object[data-object-id='#{user1.id}']").present? }
  766. find("li.recipientList-entry.js-object[data-object-id='#{user1.id}']").click
  767. find('div[data-attribute-name=organization_id] .js-input').fill_in with: user1.organizations[0].name, fill_options: { clear: :backspace }
  768. wait.until { page.all("div[data-attribute-name=organization_id] .js-option[data-value='#{user1.organizations[0].id}']").present? }
  769. page.find("div[data-attribute-name=organization_id] .js-option[data-value='#{user1.organizations[0].id}'] span").click
  770. click '.js-submit'
  771. wait.until { Ticket.last.organization_id == user1.organizations[0].id }
  772. end
  773. it 'restores saved organization selection correctly (#5347)' do
  774. find('[name=customer_id_completion]').fill_in with: user1.firstname
  775. wait.until { page.all("li.recipientList-entry.js-object[data-object-id='#{user1.id}']").present? }
  776. find("li.recipientList-entry.js-object[data-object-id='#{user1.id}']").click
  777. find('div[data-attribute-name=organization_id] .js-input').fill_in with: organization2.name, fill_options: { clear: :backspace }
  778. wait.until { page.all("div[data-attribute-name=organization_id] .js-option[data-value='#{organization2.id}']").present? }
  779. taskbar_timestamp = Taskbar.last.updated_at
  780. page.find("div[data-attribute-name=organization_id] .js-option[data-value='#{organization2.id}'] span").click
  781. wait.until { Taskbar.last.updated_at != taskbar_timestamp }
  782. refresh
  783. expect(find('div[data-attribute-name=organization_id] .js-input').value).to eq(organization2.name)
  784. end
  785. end
  786. context 'when customer' do
  787. before do
  788. visit 'customer_ticket_new'
  789. end
  790. it 'does not show the organization field for user 1', authenticated_as: :customer1 do
  791. expect(page).to have_css("div[data-attribute-name='organization_id']")
  792. end
  793. it 'does show the organization field for user 2', authenticated_as: :customer2 do
  794. expect(page).to have_no_css("div[data-attribute-name='organization_id']")
  795. end
  796. it 'can create tickets for secondary organizations', authenticated_as: :customer1 do
  797. fill_in 'Title', with: 'test'
  798. find('.richtext-content').send_keys 'test'
  799. set_tree_select_value('group_id', Group.first.name)
  800. find('div[data-attribute-name=organization_id] .js-input').fill_in with: customer1.organizations[0].name, fill_options: { clear: :backspace }
  801. wait.until { page.all("div[data-attribute-name=organization_id] .js-option[data-value='#{customer1.organizations[0].id}']").present? }
  802. page.find("div[data-attribute-name=organization_id] .js-option[data-value='#{customer1.organizations[0].id}'] span").click
  803. click '.js-submit'
  804. wait.until { Ticket.last.organization_id == customer1.organizations[0].id }
  805. end
  806. end
  807. end
  808. describe 'Wrong default values in ticket create when creating from user profile #4088' do
  809. let(:customer) { create(:customer) }
  810. before do
  811. visit "ticket/create/customer/#{customer.id}"
  812. end
  813. it 'does show the default state when creating a ticket from a user profile' do
  814. expect(page).to have_select('state_id', selected: 'open')
  815. end
  816. end
  817. describe 'Ticket templates do not save the owner attribute #4175' do
  818. let(:agent) { create(:agent, groups: [Group.first]) }
  819. let!(:template) { create(:template, :dummy_data, group: Group.first, owner: agent) }
  820. before do
  821. visit 'ticket/create'
  822. # Wait for the initial taskbar update to finish
  823. taskbar_timestamp = Taskbar.last.updated_at
  824. wait.until { Taskbar.last.updated_at != taskbar_timestamp }
  825. end
  826. it 'does set owners properly by templates and taskbars' do
  827. use_template(template)
  828. expect(page).to have_select('owner_id', selected: agent.fullname)
  829. wait.until { Taskbar.last.state['owner_id'].to_i == agent.id }
  830. refresh
  831. expect(page).to have_select('owner_id', selected: agent.fullname)
  832. end
  833. end
  834. describe 'Ticket templates resets article body #2434' do
  835. let!(:template1) { create(:template, :dummy_data, title: 'template 1', body: 'body 1') }
  836. let!(:template2) { create(:template, :dummy_data, title: 'template 2', body: 'body 2') }
  837. before do
  838. visit 'ticket/create'
  839. end
  840. it 'preserves text input from the user' do
  841. taskbar_timestamp = Taskbar.last.updated_at
  842. set_editor_field_value('body', 'foobar')
  843. wait.until { Taskbar.last.updated_at != taskbar_timestamp }
  844. use_template(template1)
  845. check_input_field_value('title', 'template 1')
  846. check_editor_field_value('body', 'foobar')
  847. end
  848. it 'allows easy switching between templates' do
  849. use_template(template1)
  850. check_input_field_value('title', 'template 1')
  851. check_editor_field_value('body', 'body 1')
  852. use_template(template2)
  853. check_input_field_value('title', 'template 2')
  854. check_editor_field_value('body', 'body 2')
  855. taskbar_timestamp = Taskbar.last.updated_at
  856. set_editor_field_value('body', 'foobar')
  857. wait.until { Taskbar.last.updated_at != taskbar_timestamp }
  858. # This time body value should be left as-is
  859. use_template(template1)
  860. check_input_field_value('title', 'template 1')
  861. check_editor_field_value('body', 'foobar')
  862. end
  863. end
  864. describe 'Ticket templates are missing pending till option #4318', time_zone: 'Europe/London' do
  865. shared_examples 'check datetime field' do
  866. shared_examples 'calculated datetime value' do
  867. it 'applies correct datetime value' do
  868. use_template(template, without_taskbar: true)
  869. check_date_field_value(field, date.strftime('%m/%d/%Y'))
  870. check_time_field_value(field, date.strftime('%H:%M'))
  871. end
  872. end
  873. context 'with static operator' do
  874. let(:operator) { 'static' }
  875. let(:date) { 3.days.from_now }
  876. let(:template_value) { date.to_datetime.to_s }
  877. it_behaves_like 'calculated datetime value'
  878. end
  879. context 'with relative operator' do
  880. let(:operator) { 'relative' }
  881. let(:template_value) { value.to_s }
  882. let(:date) do
  883. # Since front-end uses a JS-specific function to add a month value to the current date,
  884. # calculating the value here with Ruby code may lead to unexpected values.
  885. # Therefore, we use a reimplementation of the ECMAScript function instead.
  886. case range
  887. when 'month'
  888. frontend_relative_month(Time.current, value)
  889. when 'year'
  890. frontend_relative_month(Time.current, 0, year: value)
  891. when 'minute', 'hour'
  892. # Javascript disregards DST switch and simply adds hours.
  893. # Rails Time does respect DST switch and this causes off-by-one errors around DST.
  894. # Time looses time zone and DST details when converted to DateTime.
  895. # Then DateTime#advance matches Javascript DST ignorance.
  896. DateTime.current.advance range.pluralize.to_sym => value
  897. else
  898. value.send(range).from_now
  899. end
  900. end
  901. %w[minute hour day week month year].each do |range|
  902. context "with #{range} range" do
  903. let(:range) { range }
  904. let(:value) do
  905. case range
  906. when 'minute' then [*(1..120)].sample
  907. when 'hour' then [*(1..48)].sample
  908. when 'day' then [*(1..31)].sample
  909. when 'week' then [*(1..53)].sample
  910. when 'month' then [*(1..12)].sample
  911. when 'year' then [*(1..20)].sample
  912. end
  913. end
  914. it_behaves_like 'calculated datetime value'
  915. end
  916. end
  917. end
  918. end
  919. shared_examples 'check date field' do
  920. let(:date) { 5.days.from_now }
  921. let(:template_value) { date.to_datetime.to_s }
  922. it 'applies correct date value' do
  923. use_template(template, without_taskbar: true)
  924. check_date_field_value(field, date.strftime('%m/%d/%Y'))
  925. end
  926. end
  927. describe 'pending till support' do
  928. let(:field) { 'pending_time' }
  929. let(:template) do
  930. create(:template,
  931. options: {
  932. 'ticket.state_id': {
  933. value: Ticket::State.find_by(name: 'pending reminder').id.to_s,
  934. },
  935. "ticket.#{field}": {
  936. value: template_value,
  937. operator: operator,
  938. range: (range if defined? range),
  939. }
  940. })
  941. end
  942. before do
  943. freeze_time
  944. template
  945. visit '/'
  946. browser_travel_to Time.current
  947. visit 'ticket/create'
  948. end
  949. include_examples 'check datetime field'
  950. end
  951. describe 'custom attribute support' do
  952. let(:template) do
  953. create(:template,
  954. options: {
  955. "ticket.#{field}": {
  956. value: template_value,
  957. operator: (operator if defined? operator),
  958. range: (range if defined? range),
  959. }
  960. })
  961. end
  962. before :all do # rubocop:disable RSpec/BeforeAfterAll
  963. screens = {
  964. create_middle: {
  965. 'ticket.agent' => {
  966. shown: true,
  967. },
  968. },
  969. }
  970. create(:object_manager_attribute_date, name: 'test_date', display: 'test_date', screens: screens)
  971. create(:object_manager_attribute_datetime, name: 'test_datetime', display: 'test_datetime', screens: screens)
  972. ObjectManager::Attribute.migration_execute # rubocop:disable Zammad/ExistsDbStrategy
  973. end
  974. after :all do # rubocop:disable RSpec/BeforeAfterAll
  975. ObjectManager::Attribute.where(name: %w[test_date test_datetime]).destroy_all
  976. end
  977. before do
  978. freeze_time
  979. template
  980. visit '/'
  981. browser_travel_to Time.current
  982. visit 'ticket/create'
  983. end
  984. context 'with date attribute' do
  985. let(:field) { 'test_date' }
  986. include_examples 'check date field'
  987. end
  988. context 'with datetime attribute' do
  989. let(:field) { 'test_datetime' }
  990. include_examples 'check datetime field'
  991. end
  992. end
  993. end
  994. describe 'Ticket templates are missing active flag #4381' do
  995. let!(:active_template) { create(:template, :dummy_data, active: true) }
  996. let!(:inactive_template) { create(:template, :dummy_data, active: false) }
  997. before do
  998. visit 'ticket/create'
  999. end
  1000. it 'filters active templates only' do
  1001. expect(find('#form-template select[name="id"]')).to have_css('option', text: active_template.name).and(have_no_selector('option', text: inactive_template.name))
  1002. end
  1003. end
  1004. describe 'CC field cannot be customized in the new ticket templates #4421', authenticated_as: :agent do
  1005. let(:group) { create(:group) }
  1006. let(:agent) { create(:agent, groups: [group]) }
  1007. let(:customer) { create(:customer) }
  1008. let(:cc_recipients) { Array.new(3) { Faker::Internet.unique.email }.join(', ') }
  1009. let!(:template) do
  1010. create(:template,
  1011. options: {
  1012. 'ticket.title': {
  1013. value: Faker::Name.unique.name_with_middle,
  1014. },
  1015. 'ticket.customer_id': {
  1016. value: customer.id,
  1017. value_completion: "#{customer.firstname} #{customer.lastname} <#{customer.email}>",
  1018. },
  1019. 'ticket.group_id': {
  1020. value: group.id,
  1021. },
  1022. 'ticket.formSenderType': {
  1023. value: form_sender_type,
  1024. },
  1025. 'article.cc': {
  1026. value: cc_recipients,
  1027. },
  1028. 'article.body': {
  1029. value: Faker::Hacker.say_something_smart,
  1030. },
  1031. })
  1032. end
  1033. before do
  1034. visit 'ticket/create'
  1035. end
  1036. context 'with email article type' do
  1037. let(:form_sender_type) { 'email-out' }
  1038. it 'applies configured cc value' do
  1039. use_template(template)
  1040. expect(page).to have_css('label', text: 'CC')
  1041. check_input_field_value('cc', cc_recipients, visible: :all)
  1042. click '.js-submit'
  1043. expect(Ticket::Article.last).to have_attributes(cc: cc_recipients)
  1044. end
  1045. end
  1046. context 'with phone article type' do
  1047. let(:form_sender_type) { 'phone-out' }
  1048. it 'ignores configured cc value' do
  1049. use_template(template)
  1050. expect(page).to have_no_css('label', text: 'CC')
  1051. click '.js-submit'
  1052. expect(Ticket::Article.last).not_to have_attributes(cc: cc_recipients)
  1053. end
  1054. end
  1055. end
  1056. describe 'Open ticket indicator coloring setting' do
  1057. let(:elem) { find('[data-tab="customer"]') }
  1058. let(:customer) { create(:customer) }
  1059. let(:group) { Group.first }
  1060. before do
  1061. Setting.set 'ui_sidebar_open_ticket_indicator_colored', state
  1062. customer.preferences[:tickets_open] = tickets_count
  1063. customer.save!
  1064. visit 'ticket/create'
  1065. field = find '[name=customer_id]', visible: :hidden
  1066. field.execute_script "this.value = #{customer.id}"
  1067. field.execute_script '$(this).trigger("change")'
  1068. end
  1069. context 'when enabled' do
  1070. let(:state) { true }
  1071. context 'with 1 ticket' do
  1072. let(:tickets_count) { 1 }
  1073. it 'highlights as warning' do
  1074. create(:ticket, customer: customer, group: group)
  1075. expect(elem)
  1076. .to have_no_selector('.tabsSidebar-tab-count--danger')
  1077. .and have_css('.tabsSidebar-tab-count--warning')
  1078. end
  1079. end
  1080. context 'with 2 tickets' do
  1081. let(:tickets_count) { 2 }
  1082. it 'highlights as danger' do
  1083. expect(elem)
  1084. .to have_css('.tabsSidebar-tab-count--danger')
  1085. .and have_no_selector('.tabsSidebar-tab-count--warning')
  1086. end
  1087. end
  1088. end
  1089. context 'when disabled' do
  1090. let(:state) { false }
  1091. context 'with 2 tickets' do
  1092. let(:tickets_count) { 2 }
  1093. it 'does not highlight' do
  1094. expect(elem)
  1095. .to have_no_selector('.tabsSidebar-tab-count--danger, .tabsSidebar-tab-count--warning')
  1096. end
  1097. end
  1098. end
  1099. end
  1100. describe 'Not possible to select multiple values in a multi-tree select when a workflow with a select action is executed #4465', authenticated_as: :authenticate, db_strategy: :reset do
  1101. let(:field_name) { SecureRandom.uuid }
  1102. let(:screens) do
  1103. {
  1104. 'create_middle' => {
  1105. 'ticket.agent' => {
  1106. 'shown' => true,
  1107. 'required' => false,
  1108. }
  1109. }
  1110. }
  1111. end
  1112. def authenticate
  1113. create(:object_manager_attribute_multi_tree_select, name: field_name, display: field_name, screens: screens)
  1114. ObjectManager::Attribute.migration_execute
  1115. create(:core_workflow,
  1116. object: 'Ticket',
  1117. perform: {
  1118. 'ticket.priority_id': {
  1119. operator: 'select',
  1120. select: [Ticket::Priority.find_by(name: '3 high').id.to_s]
  1121. },
  1122. })
  1123. true
  1124. end
  1125. def multi_tree_select_click(value)
  1126. page.evaluate_script("document.querySelector(\"div[data-attribute-name='#{field_name}'] .js-optionsList li[data-value='#{value}'] .searchableSelect-option-text\").click()")
  1127. await_empty_ajax_queue
  1128. end
  1129. before do
  1130. visit 'ticket/create'
  1131. end
  1132. it 'does not clear the values of the multi tree select' do
  1133. multi_tree_select_click('Incident')
  1134. multi_tree_select_click('Service request')
  1135. multi_tree_select_click('Change request')
  1136. select '1 low', from: 'priority_id'
  1137. select 'pending reminder', from: 'state_id'
  1138. expect(page).to have_css('span.token-label', text: 'Incident')
  1139. expect(page).to have_css('span.token-label', text: 'Service request')
  1140. expect(page).to have_css('span.token-label', text: 'Change request')
  1141. end
  1142. end
  1143. describe 'Support title and body in core workflow actions #4519', authenticated_as: :authenticate do
  1144. let(:workflow) do
  1145. create(:core_workflow,
  1146. object: 'Ticket',
  1147. condition_selected: { 'ticket.priority_id'=>{ 'operator' => 'is', 'value' => [Ticket::Priority.find_by(name: '2 normal').id.to_s] } },
  1148. perform: { 'ticket.title' => { 'operator' => %w[fill_in set_readonly], 'fill_in' => 'title 123', 'set_readonly' => true }, 'ticket.body' => { 'operator' => %w[fill_in set_readonly], 'fill_in' => '<b>text 123</b>', 'set_readonly' => true } })
  1149. end
  1150. context 'when agent' do
  1151. def authenticate
  1152. workflow
  1153. true
  1154. end
  1155. before do
  1156. visit '#ticket/create'
  1157. end
  1158. it 'has core workflow support for title and text' do
  1159. expect(page).to have_css('div[data-attribute-name=title].is-readonly')
  1160. expect(page).to have_css('div[data-attribute-name=body].is-readonly')
  1161. expect(page.find_field('Title').value).to eq('title 123')
  1162. expect(page.find('div[data-name=body]')['innerHTML']).to eq('<b>text 123</b>')
  1163. # unset readonly (https://github.com/zammad/zammad/issues/4540)
  1164. select '1 low', from: 'priority_id'
  1165. expect(page).to have_no_css('div[data-attribute-name=title].is-readonly')
  1166. expect(page).to have_no_css('div[data-attribute-name=body].is-readonly')
  1167. # reset readonly
  1168. select '2 normal', from: 'priority_id'
  1169. expect(page).to have_css('div[data-attribute-name=title].is-readonly')
  1170. expect(page).to have_css('div[data-attribute-name=body].is-readonly')
  1171. end
  1172. end
  1173. context 'when customer' do
  1174. def authenticate
  1175. workflow
  1176. create(:customer)
  1177. end
  1178. before do
  1179. visit '#customer_ticket_new'
  1180. end
  1181. it 'has core workflow support for title and text' do
  1182. expect(page).to have_css('div[data-attribute-name=title].is-readonly')
  1183. expect(page).to have_css('div[data-attribute-name=body].is-readonly')
  1184. expect(page.find_field('Title').value).to eq('title 123')
  1185. expect(page.find('div[data-name=body]')['innerHTML']).to eq('<b>text 123</b>')
  1186. end
  1187. end
  1188. end
  1189. describe 'Date field value of a ticket template is not added into a new ticket #4864', authenticated_as: :authenticate, db_strategy: :reset do
  1190. let(:field_name) { SecureRandom.uuid }
  1191. let(:field) do
  1192. create(:object_manager_attribute_date, :required_screen, name: field_name, display: field_name)
  1193. ObjectManager::Attribute.migration_execute
  1194. end
  1195. let(:template) do
  1196. create(:template, :dummy_data).tap do |template|
  1197. template.update(options: template.options.merge("ticket.#{field_name}" => { 'operator' => 'relative', 'value' => '1', 'range' => 'day' }))
  1198. end
  1199. end
  1200. def authenticate
  1201. field
  1202. true
  1203. end
  1204. before do
  1205. visit 'ticket/create'
  1206. use_template(template)
  1207. end
  1208. it 'does create a date with a relative template value' do
  1209. click '.js-submit'
  1210. expect(Ticket.last).to have_attributes(field_name => 1.day.from_now.to_date)
  1211. end
  1212. end
  1213. describe 'Setting a group via CoreWorkflow in the ticket creation mask does not update text module filters and email signatures #4891', authenticated_as: :authenticate do
  1214. let(:group_1) { create(:group, signature: create(:signature, body: SecureRandom.uuid)) }
  1215. let(:group_2) { create(:group, signature: create(:signature, body: SecureRandom.uuid)) }
  1216. let(:workflow_1) do
  1217. create(:core_workflow,
  1218. object: 'Ticket',
  1219. condition_selected: { 'ticket.priority_id'=>{ 'operator' => 'is', 'value' => Ticket::Priority.find_by(name: '3 high').id.to_s } },
  1220. perform: { 'ticket.group_id' => { 'operator' => 'select', 'select' => group_1.id.to_s } })
  1221. end
  1222. let(:workflow_2) do
  1223. create(:core_workflow,
  1224. object: 'Ticket',
  1225. condition_selected: { 'ticket.priority_id'=>{ 'operator' => 'is', 'value' => Ticket::Priority.find_by(name: '1 low').id.to_s } },
  1226. perform: { 'ticket.group_id' => { 'operator' => 'select', 'select' => group_2.id.to_s } })
  1227. end
  1228. def authenticate
  1229. workflow_1 && workflow_2
  1230. group_1 && group_2
  1231. create(:agent, groups: Group.all)
  1232. end
  1233. it 'does change the signature when switching group via core workflow' do
  1234. visit 'ticket/create'
  1235. find('[data-type=email-out]').click
  1236. page.find('[name=priority_id]').select '3 high'
  1237. expect(page).to have_text(group_1.signature.body)
  1238. page.find('[name=priority_id]').select '1 low'
  1239. expect(page).to have_text(group_2.signature.body)
  1240. end
  1241. end
  1242. describe 'CoreWorkflow "fill in empty" fires on non-empty fields during ticket creation when logged in as customer #5004' do
  1243. let(:body_content) { SecureRandom.uuid }
  1244. let(:workflow) do
  1245. create(:core_workflow,
  1246. object: 'Ticket',
  1247. perform: { 'article.body' => { 'operator' => 'fill_in_empty', 'fill_in_empty' => body_content } })
  1248. end
  1249. def setup_workflows
  1250. workflow
  1251. end
  1252. context 'when agent', authenticated_as: :authenticate do
  1253. def authenticate
  1254. setup_workflows
  1255. true
  1256. end
  1257. before do
  1258. visit '#ticket/create'
  1259. end
  1260. it 'does fill the body' do
  1261. check_editor_field_value('body', body_content)
  1262. set_editor_field_value('body', 'new_content')
  1263. check_editor_field_value('body', 'new_content')
  1264. page.find('[name=priority_id]').select '3 high'
  1265. check_editor_field_value('body', 'new_content')
  1266. end
  1267. end
  1268. context 'when customer', authenticated_as: :authenticate do
  1269. def authenticate
  1270. setup_workflows
  1271. create(:customer)
  1272. end
  1273. before do
  1274. visit 'customer_ticket_new'
  1275. end
  1276. it 'does fill the body' do
  1277. check_editor_field_value('body', body_content)
  1278. set_editor_field_value('body', 'new_content')
  1279. check_editor_field_value('body', 'new_content')
  1280. page.find('[name=state_id]').select 'closed'
  1281. check_editor_field_value('body', 'new_content')
  1282. end
  1283. end
  1284. end
  1285. end