whatsapp_reply_spec.rb 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe 'Ticket Zoom > Whatsapp reply', :use_vcr, authenticated_as: :user, performs_jobs: true, required_envs: %w[WHATSAPP_ACCESS_TOKEN WHATSAPP_APP_SECRET WHATSAPP_BUSINESS_ID WHATSAPP_PHONE_NUMBER WHATSAPP_PHONE_NUMBER_ID WHATSAPP_PHONE_NUMBER_NAME WHATSAPP_RECIPIENT_NUMBER], type: :system do
  4. let(:article) { create(:whatsapp_article, from_phone_number: ENV['WHATSAPP_RECIPIENT_NUMBER'], ticket: ticket) }
  5. let(:ticket) { create(:whatsapp_ticket, channel: channel) }
  6. let(:user) { create(:agent, groups: [ticket.group]) }
  7. let(:sample_text) { 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' }
  8. let(:channel) do
  9. create(:whatsapp_channel,
  10. business_id: ENV['WHATSAPP_BUSINESS_ID'],
  11. access_token: ENV['WHATSAPP_ACCESS_TOKEN'],
  12. phone_number_id: ENV['WHATSAPP_PHONE_NUMBER_ID'],
  13. phone_number: ENV['WHATSAPP_PHONE_NUMBER'],
  14. name: ENV['WHATSAPP_PHONE_NUMBER_NAME'],
  15. app_secret: ENV['WHATSAPP_APP_SECRET'])
  16. end
  17. before do
  18. article
  19. visit "#ticket/zoom/#{ticket.id}"
  20. end
  21. context 'when replying to a whatsapp message' do
  22. it 'allows to reply via whatsapp' do
  23. within(:active_content) do
  24. click_on 'reply'
  25. find(:richtext).send_keys(sample_text)
  26. click '.js-submit'
  27. expect(page).to have_css('.textBubble', text: sample_text)
  28. end
  29. perform_enqueued_jobs
  30. expect(Ticket::Article.last).to have_attributes(
  31. type: have_attributes(name: 'whatsapp message'),
  32. preferences: include(
  33. delivery_status: 'success',
  34. ),
  35. )
  36. end
  37. end
  38. describe 'attachments limit' do
  39. it 'shows error message when switching from another type with multiple attachments' do
  40. within(:active_content) do
  41. find(:richtext).send_keys(sample_text)
  42. 2.times do |i|
  43. find('input#fileUpload_1', visible: :all).set(Rails.root.join("spec/fixtures/files/image/squares#{i > 1 ? i.to_s : ''}.png"))
  44. expect(page).to have_text("squares#{i > 1 ? i.to_s : ''}.png")
  45. end
  46. click '.js-selectableTypes'
  47. click '.js-articleTypeItem[data-value="whatsapp message"]'
  48. expect(page).to have_text('Only 1 attachment allowed')
  49. within first('.attachment--row') do |elem|
  50. elem.execute_script('$(".attachment-delete", this).trigger("click")')
  51. end
  52. expect(page).to have_no_text('Only 1 attachment allowed')
  53. end
  54. end
  55. it 'does not allow to upload over the limit' do
  56. within(:active_content) do
  57. click_on 'reply'
  58. find('input#fileUpload_1', visible: :all).set(Rails.root.join('spec/fixtures/files/image/squares.png'))
  59. expect(page).to have_text('squares.png')
  60. # This click tests if file upload by button is disabled successfully.
  61. # If button is not disabled, it will crash with a file dialog being opened.
  62. expect { find('.fileUpload').click(wait: 0) }
  63. .to raise_error(Selenium::WebDriver::Error::ElementClickInterceptedError)
  64. # This upload attempt simulates drag&drop which could work even with the button disabled.
  65. find('input#fileUpload_1', visible: :all).set(Rails.root.join('spec/fixtures/files/image/squares2.png'))
  66. in_modal do
  67. expect(page).to have_text('Only 1 attachment allowed')
  68. end
  69. end
  70. end
  71. end
  72. describe 'caption disabling' do
  73. let(:audio_file) do
  74. # Tempfile does not work, because it appends auto-generated extension and breaks mimetype check
  75. tmp_file_path = Rails.root.join('tmp', "#{SecureRandom.uuid}.mp3")
  76. file = File.new(tmp_file_path, 'w')
  77. file.write(sample_text)
  78. file.close
  79. file
  80. end
  81. after { File.unlink audio_file.path }
  82. it 'disables caption when specific Whatsapp attachment is present' do
  83. within(:active_content) do
  84. find(:richtext).send_keys(sample_text)
  85. expect(find(:richtext)).to not_match_css('.text-muted')
  86. find('input#fileUpload_1', visible: :all).set(audio_file.path)
  87. click '.js-selectableTypes'
  88. click '.js-articleTypeItem[data-value="whatsapp message"]'
  89. expect(find(:richtext)).to match_css('.text-muted')
  90. within first('.attachment--row') do |elem|
  91. elem.execute_script('$(".attachment-delete", this).trigger("click")')
  92. end
  93. expect(find(:richtext)).to not_match_css('.text-muted')
  94. end
  95. end
  96. end
  97. describe 'with the customer service window information' do
  98. let(:article) do
  99. create(:whatsapp_article,
  100. from_phone_number: ENV['WHATSAPP_RECIPIENT_NUMBER'],
  101. ticket: ticket,
  102. timestamp_incoming: last_whatsapp_timestamp)
  103. end
  104. context 'when the window is open' do
  105. let(:last_whatsapp_timestamp) { 30.minutes.ago.to_i.to_s }
  106. it 'shows a warning alert with the text and humanized time' do
  107. within(:active_content) do
  108. expect(find('.scrollPageAlert')).to have_no_css('.hide')
  109. .and have_css('.alert--warning')
  110. .and have_text('You have a 24 hour window to send WhatsApp messages in this conversation. The customer service window closes in 23 hours.')
  111. end
  112. end
  113. end
  114. context 'when the window is closed' do
  115. let(:last_whatsapp_timestamp) { 24.hours.ago.to_i.to_s }
  116. it 'shows a danger alert with the text' do
  117. within(:active_content) do
  118. expect(find('.scrollPageAlert')).to have_no_css('.hide')
  119. .and have_css('.alert--danger')
  120. .and have_text('The 24 hour customer service window is now closed, no further WhatsApp messages can be sent.')
  121. end
  122. end
  123. end
  124. context 'when the timestamp is missing' do
  125. let(:last_whatsapp_timestamp) { nil }
  126. it 'keeps the alert container hidden' do
  127. within(:active_content) do
  128. expect(find('.scrollPageAlert', visible: :hide)).to be_present
  129. end
  130. end
  131. end
  132. context 'when the ticket is closed' do
  133. let(:ticket) { create(:whatsapp_ticket, channel: channel, state: Ticket::State.lookup(name: 'closed')) }
  134. let(:last_whatsapp_timestamp) { 30.minutes.ago.to_i.to_s }
  135. it 'keeps the alert container hidden' do
  136. within(:active_content) do
  137. expect(find('.scrollPageAlert', visible: :hide)).to be_present
  138. end
  139. end
  140. end
  141. end
  142. describe 'when connection errors occur', authenticated_as: :authenticate do
  143. let(:article) { create(:whatsapp_article, :with_attachment_media_document, from_phone_number: ENV['WHATSAPP_RECIPIENT_NUMBER'], ticket: ticket) }
  144. def authenticate
  145. allow_any_instance_of(Whatsapp::Retry::Media).to receive(:download_media).and_return(true)
  146. article.preferences['whatsapp']['media_error'] = true
  147. article.save!
  148. create(:agent, groups: Group.all)
  149. end
  150. it 'does retry the article attachments' do
  151. expect(page).to have_text('RETRY ATTACHMENT DOWNLOAD')
  152. click '.js-retryWhatsAppAttachmentDownload'
  153. expect(page).to have_no_text('RETRY ATTACHMENT DOWNLOAD')
  154. end
  155. end
  156. end