zoom_spec.rb 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. require 'rails_helper'
  2. RSpec.describe 'Ticket zoom', type: :system do
  3. describe 'owner auto-assignment', authenticated_as: :authenticate do
  4. let!(:ticket) { create(:ticket, group: Group.find_by(name: 'Users'), state: Ticket::State.find_by(name: 'new')) }
  5. let!(:session_user) { User.find_by(login: 'master@example.com') }
  6. context 'for agent disabled' do
  7. def authenticate
  8. Setting.set('ticket_auto_assignment', false)
  9. Setting.set('ticket_auto_assignment_selector', { condition: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category(:work_on).pluck(:id) } } })
  10. Setting.set('ticket_auto_assignment_user_ids_ignore', [])
  11. true
  12. end
  13. it 'do not assign ticket to current session user' do
  14. visit "#ticket/zoom/#{ticket.id}"
  15. within(:active_content) do
  16. expect(page).to have_css('select[name=owner_id]')
  17. expect(page).to have_select('owner_id',
  18. selected: '-',
  19. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  20. end
  21. end
  22. end
  23. context 'for agent enabled' do
  24. def authenticate
  25. Setting.set('ticket_auto_assignment', true)
  26. Setting.set('ticket_auto_assignment_selector', { condition: { 'ticket.state_id' => { operator: 'is', value: Ticket::State.by_category(:work_on).pluck(:id) } } })
  27. Setting.set('ticket_auto_assignment_user_ids_ignore', setting_user_ids_ignore) if defined?(setting_user_ids_ignore)
  28. true
  29. end
  30. context 'with empty "ticket_auto_assignment_user_ids_ignore"' do
  31. it 'assigns ticket to current session user' do
  32. visit "#ticket/zoom/#{ticket.id}"
  33. within(:active_content) do
  34. expect(page).to have_css('.content.active select[name=owner_id]')
  35. expect(page).to have_select('owner_id',
  36. selected: session_user.fullname,
  37. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  38. end
  39. end
  40. end
  41. context 'with "ticket_auto_assignment_user_ids_ignore" (as integer)' do
  42. let(:setting_user_ids_ignore) { session_user.id }
  43. it 'assigns ticket not to current session user' do
  44. visit "#ticket/zoom/#{ticket.id}"
  45. within(:active_content) do
  46. expect(page).to have_css('select[name=owner_id]')
  47. expect(page).to have_select('owner_id',
  48. selected: '-',
  49. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  50. end
  51. end
  52. end
  53. context 'with "ticket_auto_assignment_user_ids_ignore" (as string)' do
  54. let(:setting_user_ids_ignore) { session_user.id.to_s }
  55. it 'assigns ticket not to current session user' do
  56. visit "#ticket/zoom/#{ticket.id}"
  57. within(:active_content) do
  58. expect(page).to have_css('select[name=owner_id]')
  59. expect(page).to have_select('owner_id',
  60. selected: '-',
  61. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  62. end
  63. end
  64. end
  65. context 'with "ticket_auto_assignment_user_ids_ignore" (as [integer])' do
  66. let(:setting_user_ids_ignore) { [session_user.id] }
  67. it 'assigns ticket not to current session user' do
  68. visit "#ticket/zoom/#{ticket.id}"
  69. within(:active_content) do
  70. expect(page).to have_css('select[name=owner_id]')
  71. expect(page).to have_select('owner_id',
  72. selected: '-',
  73. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  74. end
  75. end
  76. end
  77. context 'with "ticket_auto_assignment_user_ids_ignore" (as [string])' do
  78. let(:setting_user_ids_ignore) { [session_user.id.to_s] }
  79. it 'assigns ticket not to current session user' do
  80. visit "#ticket/zoom/#{ticket.id}"
  81. within(:active_content) do
  82. expect(page).to have_css('select[name=owner_id]')
  83. expect(page).to have_select('owner_id',
  84. selected: '-',
  85. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  86. end
  87. end
  88. end
  89. context 'with "ticket_auto_assignment_user_ids_ignore" and other user ids' do
  90. let(:setting_user_ids_ignore) { [99_999, 999_999] }
  91. it 'assigns ticket to current session user' do
  92. visit "#ticket/zoom/#{ticket.id}"
  93. within(:active_content) do
  94. expect(page).to have_css('select[name=owner_id]')
  95. expect(page).to have_select('owner_id',
  96. selected: session_user.fullname,
  97. options: ['-', 'Agent 1 Test', 'Test Master Agent'])
  98. end
  99. end
  100. end
  101. end
  102. end
  103. context 'when ticket has an attachment' do
  104. let(:group) { Group.find_by(name: 'Users') }
  105. let(:ticket) { create(:ticket, group: group) }
  106. let(:article) { create(:ticket_article, ticket: ticket) }
  107. let(:attachment_name) { 'some_file.txt' }
  108. before do
  109. Store.add(
  110. object: 'Ticket::Article',
  111. o_id: article.id,
  112. data: 'some content',
  113. filename: attachment_name,
  114. preferences: {
  115. 'Content-Type' => 'text/plain',
  116. },
  117. created_by_id: 1,
  118. )
  119. end
  120. context 'article was already forwarded once' do
  121. before do
  122. visit "#ticket/zoom/#{ticket.id}"
  123. within(:active_content) do
  124. find('a[data-type=emailForward]').click
  125. click('.js-reset')
  126. have_no_css('.js-reset')
  127. end
  128. end
  129. it 'adds attachments when forwarding multiple times' do
  130. within(:active_content) do
  131. find('a[data-type=emailForward]').click
  132. end
  133. within('.js-writeArea') do
  134. expect(page).to have_text attachment_name
  135. end
  136. end
  137. end
  138. end
  139. context 'replying' do
  140. context 'Group without signature' do
  141. let(:ticket) { create(:ticket) }
  142. let(:current_user) { create(:agent, password: 'test', groups: [ticket.group]) }
  143. before do
  144. # initial article to reply to
  145. create(:ticket_article, ticket: ticket)
  146. end
  147. it 'ensures that text input opens on multiple replies', authenticated_as: :current_user do
  148. visit "ticket/zoom/#{ticket.id}"
  149. 2.times do |article_offset|
  150. articles_existing = 1
  151. articles_expected = articles_existing + (article_offset + 1)
  152. all('a[data-type=emailReply]').last.click
  153. # wait till input box expands completely
  154. find('.attachmentPlaceholder-label').in_fixed_position
  155. expect(page).to have_no_css('.attachmentPlaceholder-hint')
  156. find('.articleNewEdit-body').send_keys('Some reply')
  157. click '.js-submit'
  158. expect(page).to have_css('.ticket-article-item', count: articles_expected)
  159. end
  160. end
  161. end
  162. context 'to inbound phone call', current_user_id: -> { agent.id }, authenticated_as: -> { agent } do
  163. let(:agent) { create(:agent, groups: [Group.first]) }
  164. let(:customer) { create(:agent) }
  165. let(:ticket) { create(:ticket, customer: customer, group: agent.groups.first) }
  166. let!(:article) { create(:ticket_article, :inbound_phone, ticket: ticket) }
  167. it 'goes to customer email' do
  168. visit "ticket/zoom/#{ticket.id}"
  169. within :active_ticket_article, article do
  170. click '.js-ArticleAction[data-type=emailReply]'
  171. end
  172. within :active_content do
  173. within '.article-new' do
  174. expect(find('[name=to]', visible: :all).value).to eq customer.email
  175. end
  176. end
  177. end
  178. end
  179. context 'to outbound phone call', current_user_id: -> { agent.id }, authenticated_as: -> { agent } do
  180. let(:agent) { create(:agent, groups: [Group.first]) }
  181. let(:customer) { create(:agent) }
  182. let(:ticket) { create(:ticket, customer: customer, group: agent.groups.first) }
  183. let!(:article) { create(:ticket_article, :outbound_phone, ticket: ticket) }
  184. it 'goes to customer email' do
  185. visit "ticket/zoom/#{ticket.id}"
  186. within :active_ticket_article, article do
  187. click '.js-ArticleAction[data-type=emailReply]'
  188. end
  189. within :active_content do
  190. within '.article-new' do
  191. expect(find('[name=to]', visible: :all).value).to eq customer.email
  192. end
  193. end
  194. end
  195. end
  196. end
  197. describe 'delete article', authenticated_as: :authenticate do
  198. let(:group) { Group.first }
  199. let(:admin) { create :admin, groups: [group] }
  200. let(:agent) { create :agent, groups: [group] }
  201. let(:other_agent) { create :agent, groups: [group] }
  202. let(:customer) { create :customer }
  203. let(:article) { send(item) }
  204. def authenticate
  205. Setting.set('ui_ticket_zoom_article_delete_timeframe', setting_delete_timeframe) if defined?(setting_delete_timeframe)
  206. article
  207. user
  208. end
  209. def article_communication
  210. create_ticket_article(sender_name: 'Agent', internal: false, type_name: 'email', updated_by: customer)
  211. end
  212. def article_note_self
  213. create_ticket_article(sender_name: 'Agent', internal: true, type_name: 'note', updated_by: user)
  214. end
  215. def article_note_other
  216. create_ticket_article(sender_name: 'Agent', internal: true, type_name: 'note', updated_by: other_agent)
  217. end
  218. def article_note_customer
  219. create_ticket_article(sender_name: 'Customer', internal: false, type_name: 'note', updated_by: customer)
  220. end
  221. def article_note_communication_self
  222. create(:ticket_article_type, name: 'note_communication', communication: true)
  223. create_ticket_article(sender_name: 'Agent', internal: true, type_name: 'note_communication', updated_by: user)
  224. end
  225. def article_note_communication_other
  226. create(:ticket_article_type, name: 'note_communication', communication: true)
  227. create_ticket_article(sender_name: 'Agent', internal: true, type_name: 'note_communication', updated_by: other_agent)
  228. end
  229. def create_ticket_article(sender_name:, internal:, type_name:, updated_by:)
  230. UserInfo.current_user_id = updated_by.id
  231. ticket = create :ticket, group: group, customer: customer
  232. create(:ticket_article,
  233. sender_name: sender_name, internal: internal, type_name: type_name, ticket: ticket,
  234. body: "to be deleted #{offset} #{item}",
  235. created_at: offset.ago, updated_at: offset.ago)
  236. end
  237. context 'going through full stack' do
  238. context 'as admin' do
  239. let(:user) { admin }
  240. let(:item) { 'article_note_self' }
  241. let(:offset) { 0.minutes }
  242. it 'succeeds' do
  243. ensure_websocket do
  244. visit "ticket/zoom/#{article.ticket.id}"
  245. end
  246. within :active_ticket_article, article do
  247. click '.js-ArticleAction[data-type=delete]'
  248. end
  249. in_modal do
  250. click '.js-submit'
  251. end
  252. wait.until_disappears { find :active_ticket_article, article, wait: false }
  253. end
  254. end
  255. end
  256. context 'verifying permissions matrix' do
  257. shared_examples 'according to permission matrix' do |item:, expects_visible:, offset:, description:|
  258. context "looking at #{description} #{item}" do
  259. let(:item) { item }
  260. let(:offset) { offset }
  261. let(:matcher) { expects_visible ? :have_css : :have_no_css }
  262. it expects_visible ? 'delete button is visible' : 'delete button is not visible' do
  263. visit "ticket/zoom/#{article.ticket.id}"
  264. within :active_ticket_article, article do
  265. expect(page).to send(matcher, '.js-ArticleAction[data-type=delete]', wait: 0)
  266. end
  267. end
  268. end
  269. end
  270. shared_examples 'deleting ticket article' do |item:, now:, later:, much_later:|
  271. include_examples 'according to permission matrix', item: item, expects_visible: now, offset: 0.minutes, description: 'just created'
  272. include_examples 'according to permission matrix', item: item, expects_visible: later, offset: 6.minutes, description: 'few minutes old'
  273. include_examples 'according to permission matrix', item: item, expects_visible: much_later, offset: 11.minutes, description: 'very old'
  274. end
  275. context 'as admin' do
  276. let(:user) { admin }
  277. include_examples 'deleting ticket article',
  278. item: 'article_communication',
  279. now: false, later: false, much_later: false
  280. include_examples 'deleting ticket article',
  281. item: 'article_note_self',
  282. now: true, later: true, much_later: false
  283. include_examples 'deleting ticket article',
  284. item: 'article_note_other',
  285. now: false, later: false, much_later: false
  286. include_examples 'deleting ticket article',
  287. item: 'article_note_customer',
  288. now: false, later: false, much_later: false
  289. include_examples 'deleting ticket article',
  290. item: 'article_note_communication_self',
  291. now: false, later: false, much_later: false
  292. include_examples 'deleting ticket article',
  293. item: 'article_note_communication_other',
  294. now: false, later: false, much_later: false
  295. end
  296. context 'as agent' do
  297. let(:user) { agent }
  298. include_examples 'deleting ticket article',
  299. item: 'article_communication',
  300. now: false, later: false, much_later: false
  301. include_examples 'deleting ticket article',
  302. item: 'article_note_self',
  303. now: true, later: true, much_later: false
  304. include_examples 'deleting ticket article',
  305. item: 'article_note_other',
  306. now: false, later: false, much_later: false
  307. include_examples 'deleting ticket article',
  308. item: 'article_note_customer',
  309. now: false, later: false, much_later: false
  310. include_examples 'deleting ticket article',
  311. item: 'article_note_communication_self',
  312. now: false, later: false, much_later: false
  313. include_examples 'deleting ticket article',
  314. item: 'article_note_communication_other',
  315. now: false, later: false, much_later: false
  316. end
  317. context 'as customer' do
  318. let(:user) { customer }
  319. include_examples 'deleting ticket article',
  320. item: 'article_communication',
  321. now: false, later: false, much_later: false
  322. include_examples 'deleting ticket article',
  323. item: 'article_note_customer',
  324. now: false, later: false, much_later: false
  325. end
  326. context 'with custom offset' do
  327. let(:setting_delete_timeframe) { 6_000 }
  328. context 'as admin' do
  329. let(:user) { admin }
  330. include_examples 'according to permission matrix', item: 'article_note_self', expects_visible: true, offset: 5000.seconds, description: 'outside of delete timeframe'
  331. include_examples 'according to permission matrix', item: 'article_note_self', expects_visible: false, offset: 8000.seconds, description: 'outside of delete timeframe'
  332. end
  333. context 'as agent' do
  334. let(:user) { agent }
  335. include_examples 'according to permission matrix', item: 'article_note_self', expects_visible: true, offset: 5000.seconds, description: 'outside of delete timeframe'
  336. include_examples 'according to permission matrix', item: 'article_note_self', expects_visible: false, offset: 8000.seconds, description: 'outside of delete timeframe'
  337. end
  338. end
  339. context 'with timeframe as 0' do
  340. let(:setting_delete_timeframe) { 0 }
  341. context 'as agent' do
  342. let(:user) { agent }
  343. include_examples 'according to permission matrix', item: 'article_note_self', expects_visible: true, offset: 99.days, description: 'long after'
  344. end
  345. end
  346. end
  347. context 'button is hidden on the go' do
  348. let(:setting_delete_timeframe) { 5 }
  349. let(:user) { agent }
  350. let(:item) { 'article_note_self' }
  351. let!(:article) { send(item) }
  352. let(:offset) { 0.seconds }
  353. it 'successfully' do
  354. visit "ticket/zoom/#{article.ticket.id}"
  355. within :active_ticket_article, article do
  356. find '.js-ArticleAction[data-type=delete]' # make sure delete button did show up
  357. expect(page).to have_no_css('.js-ArticleAction[data-type=delete]')
  358. end
  359. end
  360. end
  361. end
  362. context 'S/MIME active', authenticated_as: :authenticate do
  363. let(:system_email_address) { 'smime1@example.com' }
  364. let(:email_address) { create(:email_address, email: system_email_address) }
  365. let(:group) { create(:group, email_address: email_address) }
  366. let(:agent_groups) { [group] }
  367. let(:agent) { create(:agent, groups: agent_groups) }
  368. let(:sender_email_address) { 'smime2@example.com' }
  369. let(:customer) { create(:customer, email: sender_email_address) }
  370. let!(:ticket) { create(:ticket, group: group, owner: agent, customer: customer) }
  371. def authenticate
  372. Setting.set('smime_integration', true)
  373. agent
  374. end
  375. context 'received mail' do
  376. context 'article meta information' do
  377. context 'success' do
  378. it 'shows encryption/sign information' do
  379. create(:ticket_article, preferences: {
  380. security: {
  381. type: 'S/MIME',
  382. encryption: {
  383. success: true,
  384. comment: 'COMMENT_ENCRYPT_SUCCESS',
  385. },
  386. sign: {
  387. success: true,
  388. comment: 'COMMENT_SIGN_SUCCESS',
  389. },
  390. }
  391. }, ticket: ticket)
  392. visit "#ticket/zoom/#{ticket.id}"
  393. expect(page).to have_css('svg.icon-lock')
  394. expect(page).to have_css('svg.icon-signed')
  395. open_article_meta
  396. expect(page).to have_css('span', text: 'Encrypted')
  397. expect(page).to have_css('span', text: 'Signed')
  398. expect(page).to have_css('span[title=COMMENT_ENCRYPT_SUCCESS]')
  399. expect(page).to have_css('span[title=COMMENT_SIGN_SUCCESS]')
  400. end
  401. end
  402. context 'error' do
  403. it 'shows create information about encryption/sign failed' do
  404. create(:ticket_article, preferences: {
  405. security: {
  406. type: 'S/MIME',
  407. encryption: {
  408. success: false,
  409. comment: 'Encryption failed because XXX',
  410. },
  411. sign: {
  412. success: false,
  413. comment: 'Sign failed because XXX',
  414. },
  415. }
  416. }, ticket: ticket)
  417. visit "#ticket/zoom/#{ticket.id}"
  418. expect(page).to have_css('svg.icon-not-signed')
  419. open_article_meta
  420. expect(page).to have_css('div.alert.alert--warning', text: 'Encryption failed because XXX')
  421. expect(page).to have_css('div.alert.alert--warning', text: 'Sign failed because XXX')
  422. end
  423. end
  424. end
  425. context 'certificate not present at time of arrival' do
  426. it 'retry' do
  427. smime1 = create(:smime_certificate, :with_private, fixture: system_email_address)
  428. smime2 = create(:smime_certificate, :with_private, fixture: sender_email_address)
  429. mail = Channel::EmailBuild.build(
  430. from: sender_email_address,
  431. to: system_email_address,
  432. body: 'somebody with some text',
  433. content_type: 'text/plain',
  434. security: {
  435. type: 'S/MIME',
  436. sign: {
  437. success: true,
  438. },
  439. encryption: {
  440. success: true,
  441. },
  442. },
  443. )
  444. smime1.destroy
  445. smime2.destroy
  446. parsed_mail = Channel::EmailParser.new.parse(mail.to_s)
  447. ticket, article, _user, _mail = Channel::EmailParser.new.process({ group_id: group.id }, parsed_mail['raw'])
  448. expect(Ticket::Article.find(article.id).body).to eq('no visible content')
  449. create(:smime_certificate, fixture: sender_email_address)
  450. create(:smime_certificate, :with_private, fixture: system_email_address)
  451. visit "#ticket/zoom/#{ticket.id}"
  452. expect(page).to have_no_css('.article-content', text: 'somebody with some text')
  453. click '.js-securityRetryProcess'
  454. expect(page).to have_css('.article-content', text: 'somebody with some text')
  455. end
  456. end
  457. end
  458. context 'replying', authenticated_as: :setup_and_authenticate do
  459. def setup_and_authenticate
  460. create(:ticket_article, ticket: ticket, from: customer.email)
  461. create(:smime_certificate, :with_private, fixture: system_email_address)
  462. create(:smime_certificate, fixture: sender_email_address)
  463. authenticate
  464. end
  465. it 'plain' do
  466. visit "#ticket/zoom/#{ticket.id}"
  467. all('a[data-type=emailReply]').last.click
  468. find('.articleNewEdit-body').send_keys('Test')
  469. expect(page).to have_css('.js-securityEncrypt.btn--active', wait: 5)
  470. expect(page).to have_css('.js-securitySign.btn--active', wait: 5)
  471. click '.js-securityEncrypt'
  472. click '.js-securitySign'
  473. click '.js-submit'
  474. expect(page).to have_css('.ticket-article-item', count: 2)
  475. expect(Ticket::Article.last.preferences['security']['encryption']['success']).to be nil
  476. expect(Ticket::Article.last.preferences['security']['sign']['success']).to be nil
  477. end
  478. it 'signed' do
  479. visit "#ticket/zoom/#{ticket.id}"
  480. all('a[data-type=emailReply]').last.click
  481. find('.articleNewEdit-body').send_keys('Test')
  482. expect(page).to have_css('.js-securityEncrypt.btn--active', wait: 5)
  483. expect(page).to have_css('.js-securitySign.btn--active', wait: 5)
  484. click '.js-securityEncrypt'
  485. click '.js-submit'
  486. expect(page).to have_css('.ticket-article-item', count: 2)
  487. expect(Ticket::Article.last.preferences['security']['encryption']['success']).to be nil
  488. expect(Ticket::Article.last.preferences['security']['sign']['success']).to be true
  489. end
  490. it 'encrypted' do
  491. visit "#ticket/zoom/#{ticket.id}"
  492. all('a[data-type=emailReply]').last.click
  493. find('.articleNewEdit-body').send_keys('Test')
  494. expect(page).to have_css('.js-securityEncrypt.btn--active', wait: 5)
  495. expect(page).to have_css('.js-securitySign.btn--active', wait: 5)
  496. click '.js-securitySign'
  497. click '.js-submit'
  498. expect(page).to have_css('.ticket-article-item', count: 2)
  499. expect(Ticket::Article.last.preferences['security']['encryption']['success']).to be true
  500. expect(Ticket::Article.last.preferences['security']['sign']['success']).to be nil
  501. end
  502. it 'signed and encrypted' do
  503. visit "#ticket/zoom/#{ticket.id}"
  504. all('a[data-type=emailReply]').last.click
  505. find('.articleNewEdit-body').send_keys('Test')
  506. expect(page).to have_css('.js-securityEncrypt.btn--active', wait: 5)
  507. expect(page).to have_css('.js-securitySign.btn--active', wait: 5)
  508. click '.js-submit'
  509. expect(page).to have_css('.ticket-article-item', count: 2)
  510. expect(Ticket::Article.last.preferences['security']['encryption']['success']).to be true
  511. expect(Ticket::Article.last.preferences['security']['sign']['success']).to be true
  512. end
  513. end
  514. context 'Group default behavior' do
  515. let(:smime_config) { {} }
  516. def authenticate
  517. Setting.set('smime_integration', true)
  518. Setting.set('smime_config', smime_config)
  519. create(:ticket_article, ticket: ticket, from: customer.email)
  520. create(:smime_certificate, :with_private, fixture: system_email_address)
  521. create(:smime_certificate, fixture: sender_email_address)
  522. agent
  523. end
  524. shared_examples 'security defaults example' do |sign:, encrypt:|
  525. it "security defaults sign: #{sign}, encrypt: #{encrypt}" do
  526. within(:active_content) do
  527. encrypt_button = find('.js-securityEncrypt', wait: 5)
  528. sign_button = find('.js-securitySign', wait: 5)
  529. await_empty_ajax_queue
  530. active_button_class = '.btn--active'
  531. expect(encrypt_button.matches_css?(active_button_class, wait: 2)).to be(encrypt)
  532. expect(sign_button.matches_css?(active_button_class, wait: 2)).to be(sign)
  533. end
  534. end
  535. end
  536. shared_examples 'security defaults' do |sign:, encrypt:|
  537. before do
  538. visit "#ticket/zoom/#{ticket.id}"
  539. within(:active_content) do
  540. all('a[data-type=emailReply]').last.click
  541. find('.articleNewEdit-body').send_keys('Test')
  542. await_empty_ajax_queue
  543. end
  544. end
  545. include_examples 'security defaults example', sign: sign, encrypt: encrypt
  546. end
  547. shared_examples 'security defaults group change' do |sign:, encrypt:|
  548. before do
  549. visit "#ticket/zoom/#{ticket.id}"
  550. within(:active_content) do
  551. all('a[data-type=emailReply]').last.click
  552. find('.articleNewEdit-body').send_keys('Test')
  553. await_empty_ajax_queue
  554. select new_group.name, from: 'group_id'
  555. end
  556. end
  557. include_examples 'security defaults example', sign: sign, encrypt: encrypt
  558. end
  559. context 'not configured' do
  560. it_behaves_like 'security defaults', sign: true, encrypt: true
  561. end
  562. context 'configuration present' do
  563. let(:smime_config) do
  564. {
  565. 'group_id' => group_defaults
  566. }
  567. end
  568. let(:group_defaults) do
  569. {
  570. 'default_encryption' => {
  571. group.id.to_s => default_encryption,
  572. },
  573. 'default_sign' => {
  574. group.id.to_s => default_sign,
  575. }
  576. }
  577. end
  578. let(:default_sign) { true }
  579. let(:default_encryption) { true }
  580. shared_examples 'sign and encrypt variations' do |check_examples_name|
  581. it_behaves_like check_examples_name, sign: true, encrypt: true
  582. context 'no value' do
  583. let(:group_defaults) { {} }
  584. it_behaves_like check_examples_name, sign: true, encrypt: true
  585. end
  586. context 'signing disabled' do
  587. let(:default_sign) { false }
  588. it_behaves_like check_examples_name, sign: false, encrypt: true
  589. end
  590. context 'encryption disabled' do
  591. let(:default_encryption) { false }
  592. it_behaves_like check_examples_name, sign: true, encrypt: false
  593. end
  594. end
  595. context 'same Group' do
  596. it_behaves_like 'sign and encrypt variations', 'security defaults'
  597. end
  598. context 'Group change' do
  599. let(:new_group) { create(:group, email_address: email_address) }
  600. let(:agent_groups) { [group, new_group] }
  601. let(:group_defaults) do
  602. {
  603. 'default_encryption' => {
  604. new_group.id.to_s => default_encryption,
  605. },
  606. 'default_sign' => {
  607. new_group.id.to_s => default_sign,
  608. }
  609. }
  610. end
  611. it_behaves_like 'sign and encrypt variations', 'security defaults group change'
  612. end
  613. end
  614. end
  615. end
  616. describe 'linking Knowledge Base answer' do
  617. include_context 'basic Knowledge Base'
  618. let(:ticket) { create :ticket, group: Group.find_by(name: 'Users') }
  619. let(:answer) { published_answer }
  620. let(:translation) { answer.translations.first }
  621. shared_examples 'verify linking' do
  622. it 'allows to look up an answer' do
  623. visit "#ticket/zoom/#{ticket.id}"
  624. within :active_content do
  625. within '.link_kb_answers' do
  626. find('.js-add').click
  627. find('.js-input').send_keys translation.title
  628. find(%(li[data-value="#{translation.id}"])).click
  629. expect(find('.link_kb_answers ol')).to have_text translation.title
  630. end
  631. end
  632. end
  633. end
  634. context 'with ES', searchindex: true, authenticated_as: :authenticate do
  635. def authenticate
  636. configure_elasticsearch(required: true, rebuild: true) do
  637. answer
  638. end
  639. true
  640. end
  641. include_examples 'verify linking'
  642. end
  643. context 'without ES', authenticated_as: :authenticate do
  644. def authenticate
  645. answer
  646. true
  647. end
  648. include_examples 'verify linking'
  649. end
  650. end
  651. describe 'forwarding article with an image' do
  652. let(:ticket_article_body) do
  653. filename = 'squares.png'
  654. file = File.binread(Rails.root.join("spec/fixtures/image/#{filename}"))
  655. ext = File.extname(filename)[1...]
  656. base64 = Base64.encode64(file).delete("\n")
  657. "<img style='width: 1004px; max-width: 100%;' src=\\\"data:image/#{ext};base64,#{base64}\\\"><br>"
  658. end
  659. def current_ticket
  660. Ticket.find current_url.split('/').last
  661. end
  662. def create_ticket
  663. visit '#ticket/create'
  664. within :active_content do
  665. find('[data-type=email-out]').click
  666. find('[name=title]').fill_in with: 'Title'
  667. find('[name=customer_id_completion]').fill_in with: 'customer@example.com'
  668. find('[name=group_id]').select 'Users'
  669. find(:richtext).execute_script "this.innerHTML = \"#{ticket_article_body}\""
  670. find('.js-submit').click
  671. end
  672. await_empty_ajax_queue
  673. end
  674. def forward
  675. within :active_content do
  676. click '.js-ArticleAction[data-type=emailForward]'
  677. fill_in 'To', with: 'customer@example.com'
  678. find('.js-submit').click
  679. end
  680. await_empty_ajax_queue
  681. end
  682. def images_identical?(image_a, image_b)
  683. return false if image_a.height != image_b.height
  684. return false if image_a.width != image_b.width
  685. image_a.height.times do |y|
  686. image_a.row(y).each_with_index do |pixel, x|
  687. return false if pixel != image_b[x, y]
  688. end
  689. end
  690. true
  691. end
  692. it 'keeps image intact' do
  693. create_ticket
  694. forward
  695. images = current_ticket.articles.map do |article|
  696. ChunkyPNG::Image.from_string article.attachments.first.content
  697. end
  698. expect(images_identical?(images.first, images.second)).to be(true)
  699. end
  700. end
  701. context 'object manager attribute permission view' do
  702. let!(:group_users) { Group.find_by(name: 'Users') }
  703. shared_examples 'shows attributes and values for agent view and editable' do
  704. it 'shows attributes and values for agent view and editable', authenticated_as: :current_user do
  705. visit "ticket/zoom/#{ticket.id}"
  706. refresh # refresh to have assets generated for ticket
  707. expect(page).to have_select('state_id', options: ['new', 'open', 'pending reminder', 'pending close', 'closed'])
  708. expect(page).to have_select('priority_id')
  709. expect(page).to have_select('owner_id')
  710. expect(page).to have_css('div.tabsSidebar-tab[data-tab=customer]')
  711. end
  712. end
  713. shared_examples 'shows attributes and values for agent view but disabled' do
  714. it 'shows attributes and values for agent view but disabled', authenticated_as: :current_user do
  715. visit "ticket/zoom/#{ticket.id}"
  716. refresh # refresh to have assets generated for ticket
  717. expect(page).to have_css('select[name=state_id][disabled]')
  718. expect(page).to have_css('select[name=priority_id][disabled]')
  719. expect(page).to have_css('select[name=owner_id][disabled]')
  720. expect(page).to have_css('div.tabsSidebar-tab[data-tab=customer]')
  721. end
  722. end
  723. shared_examples 'shows attributes and values for customer view' do
  724. it 'shows attributes and values for customer view', authenticated_as: :current_user do
  725. visit "ticket/zoom/#{ticket.id}"
  726. refresh # refresh to have assets generated for ticket
  727. expect(page).to have_select('state_id', options: %w[new open closed])
  728. expect(page).to have_no_select('priority_id')
  729. expect(page).to have_no_select('owner_id')
  730. expect(page).to have_no_css('div.tabsSidebar-tab[data-tab=customer]')
  731. end
  732. end
  733. context 'as customer' do
  734. let!(:current_user) { create(:customer) }
  735. let(:ticket) { create(:ticket, customer: current_user) }
  736. include_examples 'shows attributes and values for customer view'
  737. end
  738. context 'as agent with full permissions' do
  739. let(:current_user) { create(:agent, groups: [ group_users ] ) }
  740. let(:ticket) { create(:ticket, group: group_users ) }
  741. include_examples 'shows attributes and values for agent view and editable'
  742. end
  743. context 'as agent with change permissions' do
  744. let!(:current_user) { create(:agent) }
  745. let(:ticket) { create(:ticket, group: group_users) }
  746. before do
  747. current_user.group_names_access_map = {
  748. group_users.name => %w[read change],
  749. }
  750. end
  751. include_examples 'shows attributes and values for agent view and editable'
  752. end
  753. context 'as agent with read permissions' do
  754. let!(:current_user) { create(:agent) }
  755. let(:ticket) { create(:ticket, group: group_users) }
  756. before do
  757. current_user.group_names_access_map = {
  758. group_users.name => 'read',
  759. }
  760. end
  761. include_examples 'shows attributes and values for agent view but disabled'
  762. end
  763. context 'as agent+customer with full permissions' do
  764. let!(:current_user) { create(:agent_and_customer, groups: [ group_users ] ) }
  765. context 'normal ticket' do
  766. let(:ticket) { create(:ticket, group: group_users ) }
  767. include_examples 'shows attributes and values for agent view and editable'
  768. end
  769. context 'ticket where current_user is also customer' do
  770. let(:ticket) { create(:ticket, customer: current_user, group: group_users ) }
  771. include_examples 'shows attributes and values for agent view and editable'
  772. end
  773. end
  774. context 'as agent+customer with change permissions' do
  775. let!(:current_user) { create(:agent_and_customer) }
  776. before do
  777. current_user.group_names_access_map = {
  778. group_users.name => %w[read change],
  779. }
  780. end
  781. context 'normal ticket' do
  782. let(:ticket) { create(:ticket, group: group_users) }
  783. include_examples 'shows attributes and values for agent view and editable'
  784. end
  785. context 'ticket where current_user is also customer' do
  786. let(:ticket) { create(:ticket, customer: current_user, group: group_users) }
  787. include_examples 'shows attributes and values for agent view and editable'
  788. end
  789. end
  790. context 'as agent+customer with read permissions' do
  791. let!(:current_user) { create(:agent_and_customer) }
  792. before do
  793. current_user.group_names_access_map = {
  794. group_users.name => 'read',
  795. }
  796. end
  797. context 'normal ticket' do
  798. let(:ticket) { create(:ticket, group: group_users) }
  799. include_examples 'shows attributes and values for agent view but disabled'
  800. end
  801. context 'ticket where current_user is also customer' do
  802. let(:ticket) { create(:ticket, customer: current_user, group: group_users) }
  803. include_examples 'shows attributes and values for agent view but disabled'
  804. end
  805. end
  806. context 'as agent+customer but only customer for the ticket (no agent access)' do
  807. let!(:current_user) { create(:agent_and_customer) }
  808. let(:ticket) { create(:ticket, customer: current_user) }
  809. include_examples 'shows attributes and values for customer view'
  810. end
  811. end
  812. describe 'note visibility', authenticated_as: :customer do
  813. context 'when logged in as a customer' do
  814. let(:customer) { create(:customer) }
  815. let(:ticket) { create(:ticket, customer: customer) }
  816. let!(:ticket_article) { create(:ticket_article, ticket: ticket) }
  817. let!(:ticket_note) { create(:ticket_article, ticket: ticket, internal: true, type_name: 'note') }
  818. it 'previously created private note is not visible' do
  819. visit "ticket/zoom/#{ticket_article.ticket.id}"
  820. expect(page).to have_no_selector(:active_ticket_article, ticket_note)
  821. end
  822. it 'previously created private note shows up via WS push' do
  823. visit "ticket/zoom/#{ticket_article.ticket.id}"
  824. # make sure ticket is done loading and change will be pushed via WS
  825. find(:active_ticket_article, ticket_article)
  826. await_empty_ajax_queue
  827. ticket_note.update!(internal: false)
  828. expect(page).to have_selector(:active_ticket_article, ticket_note)
  829. end
  830. end
  831. end
  832. # https://github.com/zammad/zammad/issues/3260
  833. describe 'next in overview macro changes URL', authenticated_as: :authenticate do
  834. let(:ticket_a) { create(:ticket, title: 'ticket a', group: Group.first) }
  835. let(:macro) { create(:macro, name: 'next macro', ux_flow_next_up: 'next_from_overview') }
  836. def authenticate
  837. ticket_a && macro
  838. true
  839. end
  840. it 'works' do
  841. visit 'ticket/view/all_unassigned'
  842. click_on 'Welcome to Zammad!'
  843. click '.js-openDropdownMacro'
  844. expect { find(:element_containing, macro.name).click }.to change { current_url }
  845. end
  846. end
  847. end