zoom_spec.rb 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476
  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: true, later: true, 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: true, later: true, 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(:next_ticket) { create(:ticket, title: 'next Ticket', group: Group.first) }
  835. let(:macro) { create(:macro, name: 'next macro', ux_flow_next_up: 'next_from_overview') }
  836. def authenticate
  837. next_ticket && macro
  838. true
  839. end
  840. it 'to next Ticket ID' do
  841. visit 'ticket/view/all_unassigned'
  842. click_on 'Welcome to Zammad!'
  843. click '.js-openDropdownMacro'
  844. find(:macro, macro.id).click
  845. wait(5, interval: 1).until_constant { current_url }
  846. expect(current_url).to include("ticket/zoom/#{next_ticket.id}")
  847. end
  848. end
  849. # https://github.com/zammad/zammad/issues/3279
  850. describe 'previous/next clickability when at last or first ticket' do
  851. let(:ticket_a) { create(:ticket, title: 'ticket a', group: Group.first) }
  852. let(:ticket_b) { create(:ticket, title: 'ticket b', group: Group.first) }
  853. before do
  854. ticket_a && ticket_b
  855. visit 'ticket/view/all_unassigned'
  856. end
  857. it 'previous is not clickable for the first item' do
  858. open_nth_item(0)
  859. expect { click '.pagination .previous' }.not_to change { current_url }
  860. end
  861. it 'next is clickable for the first item' do
  862. open_nth_item(0)
  863. expect { click '.pagination .next' }.to change { current_url }
  864. end
  865. it 'previous is clickable for the middle item' do
  866. open_nth_item(1)
  867. expect { click '.pagination .previous' }.to change { current_url }
  868. end
  869. it 'next is clickable for the middle item' do
  870. open_nth_item(1)
  871. expect { click '.pagination .next' }.to change { current_url }
  872. end
  873. it 'previous is clickable for the last item' do
  874. open_nth_item(2)
  875. expect { click '.pagination .previous' }.to change { current_url }
  876. end
  877. it 'next is not clickable for the last item' do
  878. open_nth_item(2)
  879. expect { click '.pagination .next' }.not_to change { current_url }
  880. end
  881. def open_nth_item(nth)
  882. within :active_content do
  883. find_all('.table tr.item .user-popover')[nth].click
  884. end
  885. end
  886. end
  887. # https://github.com/zammad/zammad/issues/3267
  888. describe 'previous/next buttons are added when open ticket is opened from overview' do
  889. let(:ticket_a) { create(:ticket, title: 'ticket a', group: Group.first) }
  890. let(:ticket_b) { create(:ticket, title: 'ticket b', group: Group.first) }
  891. # prepare an opened ticket and go to overview
  892. before do
  893. ticket_a && ticket_b
  894. visit "ticket/zoom/#{ticket_a.id}"
  895. await_empty_ajax_queue
  896. visit 'ticket/view/all_unassigned'
  897. end
  898. it 'adds previous/next buttons to existing ticket' do
  899. within :active_content do
  900. click_on ticket_a.title
  901. expect(page).to have_css('.pagination-counter')
  902. end
  903. end
  904. it 'keeps previous/next buttons when navigating to overview ticket from elsewhere' do
  905. within :active_content do
  906. click_on ticket_a.title
  907. visit 'dashboard'
  908. visit "ticket/zoom/#{ticket_a.id}"
  909. expect(page).to have_css('.pagination-counter')
  910. end
  911. end
  912. end
  913. # https://github.com/zammad/zammad/issues/2942
  914. describe 'attachments are lost in specific conditions' do
  915. let(:ticket) { create(:ticket, group: Group.first) }
  916. it 'attachment is retained when forwarding a fresh article' do
  917. ensure_websocket do
  918. visit "ticket/zoom/#{ticket.id}"
  919. end
  920. # add an article, forcing reset of form_id
  921. # click in the upper most upper left corner of the article create textbox
  922. # (that works for both Firefox and Chrome)
  923. # to avoid clicking on attachment upload
  924. find('.js-writeArea').click({ x: 5, y: 5 })
  925. # wait for propagateOpenTextarea to be completed
  926. find('.attachmentPlaceholder-label').in_fixed_position
  927. expect(page).to have_no_css('.attachmentPlaceholder-hint')
  928. # write article content
  929. find('.articleNewEdit-body').send_keys('Some reply')
  930. click '.js-submit'
  931. # wait for article to be added to the page
  932. expect(page).to have_css('.ticket-article-item', count: 1)
  933. await_empty_ajax_queue
  934. # create a on-the-fly article with attachment that will get pushed to open browser
  935. article1 = create(:ticket_article, ticket: ticket)
  936. Store.add(
  937. object: 'Ticket::Article',
  938. o_id: article1.id,
  939. data: 'some content',
  940. filename: 'some_file.txt',
  941. preferences: {
  942. 'Content-Type' => 'text/plain',
  943. },
  944. created_by_id: 1,
  945. )
  946. # wait for article to be added to the page
  947. expect(page).to have_css('.ticket-article-item', count: 2, wait: 10)
  948. await_empty_ajax_queue
  949. # click on forward of created article
  950. within :active_ticket_article, article1 do
  951. find('a[data-type=emailForward]').click
  952. end
  953. # wait for propagateOpenTextarea to be completed
  954. find('.attachmentPlaceholder-label').in_fixed_position
  955. expect(page).to have_no_css('.attachmentPlaceholder-hint')
  956. # fill forward information and create article
  957. fill_in 'To', with: 'forward@example.org'
  958. find('.articleNewEdit-body').send_keys('Forwarding with the attachment')
  959. click '.js-submit'
  960. # wait for article to be added to the page
  961. await_empty_ajax_queue
  962. expect(page).to have_css('.ticket-article-item', count: 3)
  963. # check if attachment was forwarded successfully
  964. within :active_ticket_article, ticket.reload.articles.last do
  965. within '.attachments--list' do
  966. expect(page).to have_text('some_file.txt')
  967. end
  968. end
  969. end
  970. end
  971. # https://github.com/zammad/zammad/issues/2671
  972. describe 'Pending time field in ticket sidebar', authenticated_as: :customer do
  973. let(:customer) { create(:customer) }
  974. let(:ticket) { create(:ticket, customer: customer, pending_time: 1.day.from_now, state: Ticket::State.lookup(name: 'pending reminder')) }
  975. it 'not shown to customer' do
  976. visit "ticket/zoom/#{ticket.id}"
  977. await_empty_ajax_queue
  978. within :active_content do
  979. expect(page).to have_no_css('.controls[data-name=pending_time]')
  980. end
  981. end
  982. end
  983. describe 'Pending time field in ticket sidebar as agent' do
  984. before do
  985. ticket.update(pending_time: 1.day.from_now, state: Ticket::State.lookup(name: 'pending reminder'))
  986. visit "ticket/zoom/#{ticket.id}"
  987. await_empty_ajax_queue
  988. end
  989. let(:ticket) { Ticket.first }
  990. let(:elem) { find('.js-timepicker') }
  991. # has to run asynchronously to keep both Firefox and Safari
  992. # https://github.com/zammad/zammad/issues/3414
  993. # https://github.com/zammad/zammad/issues/2887
  994. context 'when clicking timepicker component' do
  995. it 'in the first half, hours selected' do
  996. within :active_content do
  997. elem.click({ x: 10, y: 10 })
  998. expect(elem).to have_selection(0..2)
  999. end
  1000. end
  1001. it 'in the second half, minutes selected' do
  1002. within :active_content do
  1003. elem.click({ x: 30, y: 10 })
  1004. expect(elem).to have_selection(3..5)
  1005. end
  1006. end
  1007. end
  1008. matcher :have_selection do
  1009. match { starts_at == expected.begin && ends_at == expected.end }
  1010. def starts_at
  1011. actual.evaluate_script 'this.selectionStart'
  1012. end
  1013. def ends_at
  1014. actual.evaluate_script 'this.selectionEnd'
  1015. end
  1016. end
  1017. end
  1018. describe 'Article ID URL / link' do
  1019. let(:ticket) { create(:ticket, group: Group.first) }
  1020. let!(:article) { create(:'ticket/article', ticket: ticket) }
  1021. let(:url) { "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#ticket/zoom/#{ticket.id}/#{article.id}" }
  1022. it 'shows Article direct link' do
  1023. ensure_websocket do
  1024. visit "ticket/zoom/#{ticket.id}"
  1025. await_empty_ajax_queue
  1026. within :active_ticket_article, article do
  1027. expect(page).to have_css(%(a[href="#{url}"]))
  1028. end
  1029. end
  1030. end
  1031. context 'when multiple Articles are present' do
  1032. let(:article_count) { 20 }
  1033. let(:article_at_the_top) { ticket.articles.first }
  1034. let(:article_in_the_middle) { ticket.articles[ article_count / 2 ] }
  1035. let(:article_at_the_bottom) { ticket.articles.last }
  1036. before do
  1037. article_count.times do
  1038. create(:'ticket/article', ticket: ticket, body: SecureRandom.uuid)
  1039. end
  1040. end
  1041. it 'scrolls to given Article ID' do
  1042. ensure_websocket do
  1043. visit "ticket/zoom/#{ticket.id}/#{article_in_the_middle.id}"
  1044. await_empty_ajax_queue
  1045. # workaround because browser scrolls in test initially to the bottom
  1046. # maybe because the articles are not present?!
  1047. refresh
  1048. # scroll to article in the middle of the page
  1049. within :active_content do
  1050. find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
  1051. expect(find("div#article-content-#{article_at_the_top.id}")).to be_obscured
  1052. expect(find("div#article-content-#{article_in_the_middle.id}")).not_to be_obscured
  1053. expect(find("div#article-content-#{article_at_the_bottom.id}")).to be_obscured
  1054. end
  1055. # scroll to article at the top of the page
  1056. visit "ticket/zoom/#{ticket.id}/#{article_at_the_top.id}"
  1057. await_empty_ajax_queue
  1058. within :active_content do
  1059. find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
  1060. expect(find("div#article-content-#{article_at_the_top.id}")).not_to be_obscured
  1061. expect(find("div#article-content-#{article_in_the_middle.id}")).to be_obscured
  1062. expect(find("div#article-content-#{article_at_the_bottom.id}")).to be_obscured
  1063. end
  1064. # scroll to article at the bottom of the page
  1065. visit "ticket/zoom/#{ticket.id}/#{article_at_the_bottom.id}"
  1066. await_empty_ajax_queue
  1067. within :active_content do
  1068. find("div#article-content-#{article_in_the_middle.id}").in_fixed_position(wait: 0.5)
  1069. expect(find("div#article-content-#{article_at_the_top.id}")).to be_obscured
  1070. expect(find("div#article-content-#{article_in_the_middle.id}")).to be_obscured
  1071. expect(find("div#article-content-#{article_at_the_bottom.id}")).not_to be_obscured
  1072. end
  1073. end
  1074. end
  1075. end
  1076. context 'when long articles are present' do
  1077. it 'will properly show the "See more" link if you switch between the ticket and the dashboard on new articles' do
  1078. ensure_websocket do
  1079. visit "ticket/zoom/#{ticket.id}"
  1080. await_empty_ajax_queue
  1081. visit 'dashboard'
  1082. expect(page).to have_css("a.js-dashboardMenuItem[data-key='Dashboard'].is-active", wait: 10)
  1083. article_id = create(:'ticket/article', ticket: ticket, body: "#{SecureRandom.uuid} #{"lorem ipsum\n" * 200}")
  1084. expect(page).to have_css('div.tasks a.is-modified', wait: 10)
  1085. visit "ticket/zoom/#{ticket.id}"
  1086. within :active_content do
  1087. expect(find("div#article-content-#{article_id.id}")).to have_text('See more')
  1088. end
  1089. end
  1090. end
  1091. end
  1092. end
  1093. describe 'Macros', authenticated_as: :authenticate do
  1094. let(:macro) { create :macro, perform: { 'article.note'=>{ 'body' => 'macro <b>body</b>', 'internal' => 'true', 'subject' => 'macro note' } } }
  1095. let!(:ticket) { create(:ticket, group: Group.find_by(name: 'Users')) }
  1096. def authenticate
  1097. macro
  1098. true
  1099. end
  1100. it 'does html macro by default' do
  1101. visit "ticket/zoom/#{ticket.id}"
  1102. find('.js-openDropdownMacro').click
  1103. all('.js-dropdownActionMacro').last.click
  1104. await_empty_ajax_queue
  1105. expect(ticket.reload.articles.last.body).to eq('macro <b>body</b>')
  1106. expect(ticket.reload.articles.last.content_type).to eq('text/html')
  1107. end
  1108. end
  1109. describe 'object manager attributes maxlength', authenticated_as: :authenticate, db_strategy: :reset do
  1110. let(:ticket) { create(:ticket, group: Group.find_by(name: 'Users')) }
  1111. def authenticate
  1112. ticket
  1113. create :object_manager_attribute_text, name: 'maxtest', display: 'maxtest', screens: attributes_for(:required_screen), data_option: {
  1114. 'type' => 'text',
  1115. 'maxlength' => 3,
  1116. 'null' => true,
  1117. 'translate' => false,
  1118. 'default' => '',
  1119. 'options' => {},
  1120. 'relation' => '',
  1121. }
  1122. ObjectManager::Attribute.migration_execute
  1123. true
  1124. end
  1125. it 'checks ticket zoom' do
  1126. visit "ticket/zoom/#{ticket.id}"
  1127. within(:active_content) do
  1128. fill_in 'maxtest', with: 'hellu'
  1129. expect(page.find_field('maxtest').value).to eq('hel')
  1130. end
  1131. end
  1132. end
  1133. end