# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ require 'rails_helper' require 'system/apps/mobile_old/examples/create_article_examples' require 'system/apps/mobile_old/examples/article_security_examples' RSpec.describe 'Mobile > Ticket > Article > Create', app: :mobile, authenticated_as: :agent, type: :system do let(:group) { Group.find_by(name: 'Users') } let(:agent) { create(:agent, groups: [group]) } let(:customer) { create(:customer) } let(:ticket) { create(:ticket, customer: customer, group: group, owner: agent) } def wait_for_ticket_edit(number: 1) wait_for_mutation('ticketUpdate', number: number) end def save_article(number: 1) find_button('Done').click wait_for_test_flag('ticket-article-reply.closed') find_button('Save').click wait_for_ticket_edit(number: number) end def open_article_dialog visit "/tickets/#{ticket.id}" wait_for_form_to_settle('form-ticket-edit') find_button('Add reply').click end context 'when creating a new article as an agent', authenticated_as: :agent do it 'disables the done button when the form is not dirty' do open_article_dialog expect(find_button('Done', disabled: true).disabled?).to be(true) end it 'enables the done button when the form is dirty' do open_article_dialog find_editor('Text').type('foobar') expect(find_button('Done').disabled?).to be(false) end it 'creates an internal note (default)' do open_article_dialog expect(find_select('Channel', visible: :all)).to have_selected_option('Note') expect(find_select('Visibility', visible: :all)).to have_selected_option('Internal') text = find_editor('Text') expect(text).to have_text_value('', exact: true) text.type('This is a note') save_article expect(Ticket::Article.last).to have_attributes( type_id: Ticket::Article::Type.lookup(name: 'note').id, internal: true, content_type: 'text/html', sender: Ticket::Article::Sender.lookup(name: 'Agent'), body: '
This is a note
', ) end it 'doesn\'t show "save" button when part of ticket is changed and article is added' do visit "/tickets/#{ticket.id}/information" wait_for_form_to_settle('form-ticket-edit') find_input('Ticket title').type('foobar') click_on('Add reply') expect(find_select('Channel', visible: :all)).to have_selected_option('Note') expect(find_select('Visibility', visible: :all)).to have_selected_option('Internal') text = find_editor('Text') expect(text).to have_text_value('', exact: true) text.type('This is a note') save_article expect(page).to have_no_button('Save') end it 'creates a public note' do open_article_dialog find_select('Visibility', visible: :all).select_option('Public') text = find_editor('Text') expect(text).to have_text_value('', exact: true) text.type('This is a note!') save_article expect(Ticket::Article.last).to have_attributes( type_id: Ticket::Article::Type.lookup(name: 'note').id, internal: false, content_type: 'text/html', body: 'This is a note!
', ) end context 'when creating an email' do let(:signature) { create(:signature, active: true, body: "\#{user.firstname}This is a note!
#{agent.firstname}
Signature!
This is a note!
#{agent.firstname}
Signature!
This is a note!
', ) end context 'when creating a phone article' do include_examples 'mobile app: create article', 'Phone', attachments: true, conditional: false do let(:article) { create(:ticket_article, :outbound_phone, ticket: ticket) } let(:type) { Ticket::Article::Type.lookup(name: 'phone') } let(:content_type) { 'text/html' } end end context 'when creating sms article' do include_examples 'mobile app: create article', 'Sms', conditional: true do let(:article) do create( :ticket_article, ticket: ticket, type: Ticket::Article::Type.find_by(name: 'sms'), ) end let(:type) { Ticket::Article::Type.lookup(name: 'sms') } let(:content_type) { 'text/plain' } end end context 'when creating telegram article' do include_examples 'mobile app: create article', 'Telegram', attachments: true do let(:article) do create( :ticket_article, ticket: ticket, type: Ticket::Article::Type.find_by(name: 'telegram personal-message'), ) end let(:type) { Ticket::Article::Type.lookup(name: 'telegram personal-message') } let(:content_type) { 'text/plain' } end end context 'when replying to twitter status ticket' do include_examples 'mobile app: create article', 'Twitter', attachments: false do let(:article) do create( :twitter_article, ticket: ticket, sender: Ticket::Article::Sender.lookup(name: 'Customer'), ) end let(:type) { Ticket::Article::Type.lookup(name: 'twitter status') } let(:content_type) { 'text/plain' } let(:result_text) { "#{new_text}\n/#{agent.firstname.first}#{agent.lastname.first}" } end end context 'when replying to twitter dm ticket' do include_examples 'mobile app: create article', 'Twitter', attachments: false do let(:article) do create( :twitter_dm_article, ticket: ticket, sender: Ticket::Article::Sender.lookup(name: 'Customer'), ) end let(:type) { Ticket::Article::Type.lookup(name: 'twitter direct-message') } let(:content_type) { 'text/plain' } let(:to) { article.from } let(:result_text) { "#{new_text}\n/#{agent.firstname.first}#{agent.lastname.first}" } end end context 'when replying to a facebook post' do include_examples 'mobile app: create article', 'Facebook', attachments: false do let(:article) do create( :ticket_article, ticket: ticket, sender: Ticket::Article::Sender.lookup(name: 'Customer'), type: Ticket::Article::Type.lookup(name: 'facebook feed post'), ) end let(:type) { Ticket::Article::Type.lookup(name: 'facebook feed comment') } let(:content_type) { 'text/plain' } end end context 'when using suggestions' do let(:text_option) do content = "Hello, \#{ticket.customer.firstname}!" content += " Ticket \#{ticket.title} has group \#{ticket.group.name}." create( :text_module, name: 'test', content: content ) end it 'text suggestion parses correctly' do create(:ticket_article, ticket: ticket) open_article_dialog find_editor('Text').type('::test') find('[role="option"]', text: text_option.name).click body = "Hello, #{ticket.customer.firstname}!" body += " Ticket #{ticket.title} has group #{ticket.group.name}." expect(find_editor('Text')).to have_text(body) end end # TODO: test security settings end context 'when creating a new article as a customer', authenticated_as: :customer do it 'creates an article with web type' do open_article_dialog text = find_editor('Text') expect(text).to have_text_value('', exact: true) text.type('This is a note') save_article expect(Ticket::Article.last).to have_attributes( type_id: Ticket::Article::Type.lookup(name: 'web').id, internal: false, content_type: 'text/html', sender: Ticket::Article::Sender.lookup(name: 'Customer'), body: 'This is a note
', ) end end context 'when creating secured article', authenticated_as: :authenticate do def prepare_phone_article open_article_dialog find_select('Channel', visible: :all).select_option('Phone') end def prepare_email_article(with_body: false) open_article_dialog find_select('Channel', visible: :all).select_option('Email') find_autocomplete('To').search_for_option(customer.email, label: customer.fullname) find_editor('Text').type(Faker::Hacker.say_something_smart) if with_body end def submit_form save_article end it_behaves_like 'mobile app: article security', integration: :smime it_behaves_like 'mobile app: article security', integration: :pgp end context 'when inlining an image', authenticated_as: :agent do it 'correctly compresses image' do open_article_dialog find_editor('Text').type(Faker::Hacker.say_something_smart) click_on('Add image') # inserts an invisible input find('[data-test-id="editor-image-input"]', visible: :all).attach_file(Rails.root.join('spec/fixtures/files/image/large2.png')) wait_for_test_flag('editor.imageResized') save_article # The fize will always be less than it actually is even without resizing # Chrome has the best compression, so we check that actual value is lower than Firefox's compresssion expect(Store.last.size.to_i).to be <= 25_686 end end end