refactor(contact): form is persisted in db before pushed to HS

This commit is contained in:
Colin Darie 2024-07-30 19:14:04 +02:00
parent 4bc0a04106
commit 5af32b46f4
No known key found for this signature in database
GPG key ID: 4FB865FDBCA4BCC4
10 changed files with 236 additions and 290 deletions

View file

@ -2,25 +2,31 @@ class SupportController < ApplicationController
invisible_captcha only: [:create], on_spam: :redirect_to_root
def index
@form = Helpscout::Form.new(tags: tags_from_query_params, dossier_id: dossier&.id, current_user:)
@form = ContactForm.new(tags: support_form_params.fetch(:tags, []), dossier_id: dossier&.id)
@form.user = current_user
end
def admin
@form = Helpscout::Form.new(tags: tags_from_query_params, current_user:, for_admin: true)
@form = ContactForm.new(tags: support_form_params.fetch(:tags, []), for_admin: true)
@form.user = current_user
end
def create
if direct_message? && create_commentaire
if direct_message?
create_commentaire!
flash.notice = "Votre message a été envoyé sur la messagerie de votre dossier."
redirect_to messagerie_dossier_path(dossier)
return
end
@form = Helpscout::Form.new(support_form_params.except(:piece_jointe).merge(current_user:))
form_params = support_form_params
@form = ContactForm.new(form_params.except(:piece_jointe))
@form.piece_jointe.attach(form_params[:piece_jointe]) if form_params[:piece_jointe].present?
@form.user = current_user
if @form.valid?
create_conversation_later(@form)
if @form.save
@form.create_conversation_later
flash.notice = "Votre message a été envoyé."
redirect_to root_path
@ -32,29 +38,7 @@ class SupportController < ApplicationController
private
def create_conversation_later(form)
if support_form_params[:piece_jointe].present?
blob = ActiveStorage::Blob.create_and_upload!(
io: support_form_params[:piece_jointe].tempfile,
filename: support_form_params[:piece_jointe].original_filename,
content_type: support_form_params[:piece_jointe].content_type,
identify: false
).tap(&:scan_for_virus_later)
end
HelpscoutCreateConversationJob.perform_later(
blob_id: blob&.id,
subject: form.subject,
email: current_user&.email || form.email,
phone: form.phone,
text: form.text,
dossier_id: form.dossier_id,
browser: browser_name,
tags: form.tags_array
)
end
def create_commentaire
def create_commentaire!
attributes = {
piece_jointe: support_form_params[:piece_jointe],
body: "[#{support_form_params[:subject]}]<br><br>#{support_form_params[:text]}"
@ -68,12 +52,11 @@ class SupportController < ApplicationController
end
end
def tags_from_query_params
support_form_params[:tags]&.join(",") || ""
end
def direct_message?
user_signed_in? && support_form_params[:type] == Helpscout::Form::TYPE_INSTRUCTION && dossier.present? && dossier.messagerie_available?
return false unless user_signed_in?
return false unless support_form_params[:question_type] == ContactForm::TYPE_INSTRUCTION
dossier&.messagerie_available?
end
def dossier
@ -85,9 +68,9 @@ class SupportController < ApplicationController
end
def support_form_params
keys = [:email, :subject, :text, :type, :dossier_id, :piece_jointe, :phone, :tags, :for_admin]
if params.key?(:helpscout_form) # submitting form
params.require(:helpscout_form).permit(*keys)
keys = [:email, :subject, :text, :question_type, :dossier_id, :piece_jointe, :phone, :for_admin, tags: []]
if params.key?(:contact_form) # submitting form
params.require(:contact_form).permit(*keys)
else
params.permit(:dossier_id, tags: []) # prefilling form
end

View file

@ -8,41 +8,49 @@ class HelpscoutCreateConversationJob < ApplicationJob
retry_on FileNotScannedYetError, wait: :exponentially_longer, attempts: 10
attr_reader :contact_form
attr_reader :api
def perform(blob_id: nil, **params)
if blob_id.present?
blob = ActiveStorage::Blob.find(blob_id)
raise FileNotScannedYetError if blob.virus_scanner.pending?
def perform(contact_form)
@contact_form = contact_form
blob = nil unless blob.virus_scanner.safe?
if contact_form.piece_jointe.attached?
raise FileNotScannedYetError if contact_form.piece_jointe.virus_scanner.pending?
end
@api = Helpscout::API.new
create_conversation(params, blob)
create_conversation
contact_form.destroy
end
private
def create_conversation(params, blob)
def create_conversation
response = api.create_conversation(
params[:email],
params[:subject],
params[:text],
blob
contact_form.email,
contact_form.subject,
contact_form.text,
safe_blob
)
if response.success?
conversation_id = response.headers['Resource-ID']
if params[:phone].present?
api.add_phone_number(params[:email], params[:phone])
if contact_form.phone.present?
api.add_phone_number(contact_form.email, contact_form.phone)
end
api.add_tags(conversation_id, params[:tags])
api.add_tags(conversation_id, contact_form.tags)
else
fail "Error while creating conversation: #{response.response_code} '#{response.body}'"
end
end
def safe_blob
return if !contact_form.piece_jointe.virus_scanner&.safe?
contact_form.piece_jointe
end
end

View file

@ -1,78 +0,0 @@
class Helpscout::Form
include ActiveModel::Model
include ActiveModel::Attributes
attribute :email, :string
attribute :subject, :string
attribute :text, :string
attribute :type, :string
attribute :dossier_id, :integer
attribute :tags, :string
attribute :phone, :string
attribute :tags, :string
attribute :for_admin, :boolean, default: false
validates :email, presence: true, strict_email: true, if: :require_email? # i18n-tasks-use t('activemodel.errors.models.helpscout/form.invalid_email_format')
validates :subject, presence: true
validates :text, presence: true
validates :type, presence: true
attr_reader :current_user
attr_reader :options
TYPE_INFO = 'procedure_info'
TYPE_PERDU = 'lost_user'
TYPE_INSTRUCTION = 'instruction_info'
TYPE_AMELIORATION = 'product'
TYPE_AUTRE = 'other'
ADMIN_TYPE_RDV = 'admin_demande_rdv'
ADMIN_TYPE_QUESTION = 'admin_question'
ADMIN_TYPE_SOUCIS = 'admin_soucis'
ADMIN_TYPE_PRODUIT = 'admin_suggestion_produit'
ADMIN_TYPE_DEMANDE_COMPTE = 'admin_demande_compte'
ADMIN_TYPE_AUTRE = 'admin_autre'
def self.default_options
[
[I18n.t(:question, scope: [:support, :index, TYPE_INFO]), TYPE_INFO, I18n.t("links.common.faq.contacter_service_en_charge_url")],
[I18n.t(:question, scope: [:support, :index, TYPE_PERDU]), TYPE_PERDU, LISTE_DES_DEMARCHES_URL],
[I18n.t(:question, scope: [:support, :index, TYPE_INSTRUCTION]), TYPE_INSTRUCTION, I18n.t("links.common.faq.ou_en_est_mon_dossier_url")],
[I18n.t(:question, scope: [:support, :index, TYPE_AMELIORATION]), TYPE_AMELIORATION, FEATURE_UPVOTE_URL],
[I18n.t(:question, scope: [:support, :index, TYPE_AUTRE]), TYPE_AUTRE]
]
end
def self.admin_options
[
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_QUESTION], app_name: Current.application_name), ADMIN_TYPE_QUESTION],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_RDV], app_name: Current.application_name), ADMIN_TYPE_RDV],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_SOUCIS], app_name: Current.application_name), ADMIN_TYPE_SOUCIS],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_PRODUIT]), ADMIN_TYPE_PRODUIT],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_DEMANDE_COMPTE]), ADMIN_TYPE_DEMANDE_COMPTE],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_AUTRE]), ADMIN_TYPE_AUTRE]
]
end
def initialize(params)
@current_user = params.delete(:current_user)
params[:email] = EmailSanitizableConcern::EmailSanitizer.sanitize(params[:email]) if params[:email].present?
super(params)
@options = if for_admin?
self.class.admin_options
else
self.class.default_options
end
end
alias for_admin? for_admin
def tags_array
(tags&.split(",") || []) + ['contact form', type]
end
def require_email? = current_user.blank?
def persisted? = false
end

View file

@ -1,2 +1,81 @@
class ContactForm < ApplicationRecord
attr_reader :options
belongs_to :user, optional: true, dependent: :destroy
after_initialize :set_options
before_validation :normalize_strings
before_validation :sanitize_email
before_save :add_default_tags
validates :email, presence: true, strict_email: true, if: :require_email?
validates :subject, presence: true
validates :text, presence: true
validates :question_type, presence: true
has_one_attached :piece_jointe
TYPE_INFO = 'procedure_info'
TYPE_PERDU = 'lost_user'
TYPE_INSTRUCTION = 'instruction_info'
TYPE_AMELIORATION = 'product'
TYPE_AUTRE = 'other'
ADMIN_TYPE_RDV = 'admin_demande_rdv'
ADMIN_TYPE_QUESTION = 'admin_question'
ADMIN_TYPE_SOUCIS = 'admin_soucis'
ADMIN_TYPE_PRODUIT = 'admin_suggestion_produit'
ADMIN_TYPE_DEMANDE_COMPTE = 'admin_demande_compte'
ADMIN_TYPE_AUTRE = 'admin_autre'
def self.default_options
[
[I18n.t(:question, scope: [:support, :index, TYPE_INFO]), TYPE_INFO, I18n.t("links.common.faq.contacter_service_en_charge_url")],
[I18n.t(:question, scope: [:support, :index, TYPE_PERDU]), TYPE_PERDU, LISTE_DES_DEMARCHES_URL],
[I18n.t(:question, scope: [:support, :index, TYPE_INSTRUCTION]), TYPE_INSTRUCTION, I18n.t("links.common.faq.ou_en_est_mon_dossier_url")],
[I18n.t(:question, scope: [:support, :index, TYPE_AMELIORATION]), TYPE_AMELIORATION, FEATURE_UPVOTE_URL],
[I18n.t(:question, scope: [:support, :index, TYPE_AUTRE]), TYPE_AUTRE]
]
end
def self.admin_options
[
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_QUESTION], app_name: Current.application_name), ADMIN_TYPE_QUESTION],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_RDV], app_name: Current.application_name), ADMIN_TYPE_RDV],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_SOUCIS], app_name: Current.application_name), ADMIN_TYPE_SOUCIS],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_PRODUIT]), ADMIN_TYPE_PRODUIT],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_DEMANDE_COMPTE]), ADMIN_TYPE_DEMANDE_COMPTE],
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_AUTRE]), ADMIN_TYPE_AUTRE]
]
end
def for_admin=(value)
super(value)
set_options
end
def create_conversation_later
HelpscoutCreateConversationJob.perform_later(self)
end
def require_email? = user.blank?
private
def normalize_strings
self.subject = subject&.strip
self.text = text&.strip
end
def sanitize_email
self.email = EmailSanitizableConcern::EmailSanitizer.sanitize(email) if email.present?
end
def add_default_tags
self.tags = tags.push('contact form', question_type).uniq
end
def set_options
@options = for_admin? ? self.class.admin_options : self.class.default_options
end
end

View file

@ -3,23 +3,23 @@
- if form.require_email?
= render Dsfr::InputComponent.new(form: f, attribute: :email, input_type: :email_field, opts: { autocomplete: 'email' }) do |c|
- c.with_label { Helpscout::Form.human_attribute_name(form.for_admin? ? :email_pro : :email) }
- c.with_label { ContactForm.human_attribute_name(form.for_admin? ? :email_pro : :email) }
%fieldset.fr-fieldset{ name: "type" }
%fieldset.fr-fieldset{ name: "question_type" }
%legend.fr-fieldset__legend.fr-fieldset__legend--regular
= t('.your_question')
= render EditableChamp::AsteriskMandatoryComponent.new
.fr-fieldset__content
- form.options.each do |(question, question_type, link)|
.fr-radio-group
= f.radio_button :type, question_type, required: true, data: {"support-target": "inputRadio" }, checked: question_type == form.type
= f.label "type_#{question_type}", { 'aria-controls': link ? "card-#{question_type}" : nil, class: 'fr-label' } do
= f.radio_button :question_type, question_type, required: true, data: {"support-target": "inputRadio" }, checked: question_type == form.question_type
= f.label "question_type_#{question_type}", { 'aria-controls': link ? "card-#{question_type}" : nil, class: 'fr-label' } do
= question
- if link.present?
.fr-ml-3w{ id: "card-#{question_type}",
class: class_names('hidden' => question_type != form.type),
"aria-hidden": question_type != form.type,
class: class_names('hidden' => question_type != form.question_type),
"aria-hidden": question_type != form.question_type,
data: { "support-target": "content" } }
= render Dsfr::CalloutComponent.new(title: t('.our_answer')) do |c|
- c.with_html_body do
@ -43,13 +43,15 @@
%span.fr-hint-text
= t('.notice_upload_group')
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::Form::TYPE_AMELIORATION } }
%p.notice.hidden{ data: { 'contact-type-only': ContactForm::TYPE_AMELIORATION } }
= t('.notice_pj_product')
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::Form::TYPE_AUTRE } }
%p.notice.hidden{ data: { 'contact-type-only': ContactForm::TYPE_AUTRE } }
= t('.notice_pj_other')
= f.file_field :piece_jointe, class: 'fr-upload', accept: '.jpg, .jpeg, .png, .pdf'
= f.hidden_field :tags
- f.object.tags.each_with_index do |tag, index|
= f.hidden_field :tags, name: f.field_name(:tags, multiple: true), id: f.field_id(:tag, index), value: tag
= f.hidden_field :for_admin
= invisible_captcha

View file

@ -9,69 +9,4 @@
.fr-highlight= t('.intro_html')
.description
.recommandations
= t('.intro_html')
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
- if !user_signed_in?
.fr-input-group
= label_tag :email, class: 'fr-label' do
Email
= render EditableChamp::AsteriskMandatoryComponent.new
%span.fr-hint-text
= t('.notice_email')
= email_field_tag :email, params[:email], required: true, autocomplete: 'email', class: 'fr-input'
%fieldset.fr-fieldset{ name: "type" }
%legend.fr-fieldset__legend
= t('.your_question')
= render EditableChamp::AsteriskMandatoryComponent.new
.fr-fieldset__content
- @options.each do |(question, question_type, link)|
.fr-radio-group
= radio_button_tag :type, question_type, false, required: true, data: {"support-target": "inputRadio" }
= label_tag "type_#{question_type}", { 'aria-controls': link ? "card-#{question_type}" : nil, class: 'fr-label' } do
= question
- if link.present?
.fr-ml-3w.hidden{ id: "card-#{question_type}", "aria-hidden": true , data: { "support-target": "content" } }
= render Dsfr::CalloutComponent.new(title: t('.our_answer')) do |c|
- c.with_html_body do
-# i18n-tasks-use t("support.index.#{question_type}.answer_html")
= t('answer_html', scope: [:support, :index, question_type], base_url: Current.application_base_url, "link_#{question_type}": link)
.fr-input-group
= label_tag :dossier_id, t('file_number', scope: [:utils]), class: 'fr-label'
= text_field_tag :dossier_id, @dossier_id, class: 'fr-input'
.fr-input-group
= label_tag :subject, class: 'fr-label' do
= t('subject', scope: [:utils])
= render EditableChamp::AsteriskMandatoryComponent.new
= text_field_tag :subject, params[:subject], required: true, class: 'fr-input'
.fr-input-group
= label_tag :text, class: 'fr-label' do
= t('message', scope: [:utils])
= render EditableChamp::AsteriskMandatoryComponent.new
= text_area_tag :text, params[:text], rows: 6, required: true, class: 'fr-input'
.fr-upload-group
= label_tag :piece_jointe, class: 'fr-label' do
= t('pj', scope: [:utils])
%span.fr-hint-text
= t('.notice_upload_group')
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } }
= t('.notice_pj_product')
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } }
= t('.notice_pj_other')
= file_field_tag :piece_jointe, class: 'fr-upload', accept: '.jpg, .jpeg, .png, .pdf'
= hidden_field_tag :tags, @tags&.join(',')
= invisible_captcha
.send-wrapper.fr-my-3w
= button_tag t('send_mail', scope: [:utils]), type: :submit, class: 'fr-btn send'
= render partial: "form", object: @form

View file

@ -1,7 +1,7 @@
en:
activemodel:
activerecord:
attributes:
helpscout/form:
contact_form:
email: 'Your email address'
email_pro: Professional email address
phone: Professional phone number (direct line)
@ -12,15 +12,10 @@ en:
email: 'Example: address@mail.com'
errors:
models:
helpscout/form:
contact_form:
invalid_email_format: 'is not valid'
support:
index:
contact: Contact
intro_html:
'<p>Contact us via this form and we will answer you as quickly as possible.</p>
<p>Make sure you provide all the required information so we can help you in the best way.</p>'
notice_email: 'Expected format: address@mail.com'
form:
your_question: Your question
our_answer: Our answer
notice_pj_product: A screenshot can help us identify the element to improve.

View file

@ -1,7 +1,7 @@
fr:
activemodel:
activerecord:
attributes:
helpscout/form:
contact_form:
email: 'Votre adresse email'
email_pro: Votre adresse email professionnelle
phone: Numéro de téléphone professionnel (ligne directe)
@ -12,15 +12,10 @@ fr:
email: 'Exemple: adresse@mail.com'
errors:
models:
helpscout/form:
contact_form:
invalid_email_format: 'est invalide'
support:
index:
contact: Contact
intro_html:
'<p>Contactez-nous via ce formulaire et nous vous répondrons dans les plus brefs délais.</p>
<p>Pensez bien à nous donner le plus dinformations possible pour que nous puissions vous aider au mieux.</p>'
notice_email: 'Format attendu : adresse@mail.com'
form:
your_question: Votre question
our_answer: Notre réponse
notice_pj_product: Une capture décran peut nous aider à identifier plus facilement lendroit à améliorer.

View file

@ -1,4 +1,4 @@
describe SupportController, type: :controller do
describe SupportController, question_type: :controller do
render_views
context 'signed in' do
@ -45,22 +45,30 @@ describe SupportController, type: :controller do
get :index, params: { tags: tags }
expect(response.status).to eq(200)
expect(response.body).to include(tags.join(','))
expect(response.body).to include("value=\"yolo\"")
expect(response.body).to include("value=\"toto\"")
end
end
describe "send form" do
subject do
post :create, params: { helpscout_form: params }
post :create, params: { contact_form: params }
end
context "when invisible captcha is ignored" do
let(:params) { { subject: 'bonjour', text: 'un message', type: 'procedure_info' } }
let(:params) { { subject: 'bonjour', text: 'un message', question_type: 'procedure_info' } }
it 'creates a conversation on HelpScout' do
expect { subject }.to \
change(Commentaire, :count).by(0).and \
have_enqueued_job(HelpscoutCreateConversationJob).with(hash_including(params.except(:type)))
change(ContactForm, :count).by(1)
contact_form = ContactForm.last
expect(HelpscoutCreateConversationJob).to have_been_enqueued.with(contact_form)
expect(contact_form.subject).to eq("bonjour")
expect(contact_form.text).to eq("un message")
expect(contact_form.tags).to include("procedure_info")
expect(flash[:notice]).to match('Votre message a été envoyé.')
expect(response).to redirect_to root_path
@ -73,7 +81,7 @@ describe SupportController, type: :controller do
let(:params) do
{
dossier_id: dossier.id,
type: Helpscout::Form::TYPE_INSTRUCTION,
question_type: ContactForm::TYPE_INSTRUCTION,
subject: 'bonjour',
text: 'un message'
}
@ -82,7 +90,11 @@ describe SupportController, type: :controller do
it 'creates a conversation on HelpScout' do
expect { subject }.to \
change(Commentaire, :count).by(0).and \
have_enqueued_job(HelpscoutCreateConversationJob).with(hash_including(subject: 'bonjour', dossier_id: dossier.id))
change(ContactForm, :count).by(1)
contact_form = ContactForm.last
expect(HelpscoutCreateConversationJob).to have_been_enqueued.with(contact_form)
expect(contact_form.dossier_id).to eq(dossier.id)
expect(flash[:notice]).to match('Votre message a été envoyé.')
expect(response).to redirect_to root_path
@ -96,7 +108,7 @@ describe SupportController, type: :controller do
let(:params) do
{
dossier_id: dossier.id,
type: Helpscout::Form::TYPE_INSTRUCTION,
question_type: ContactForm::TYPE_INSTRUCTION,
subject: 'bonjour',
text: 'un message'
}
@ -120,7 +132,7 @@ describe SupportController, type: :controller do
context "when invisible captcha is filled" do
subject do
post :create, params: {
helpscout_form: { subject: 'bonjour', text: 'un message', type: 'procedure_info' },
contact_form: { subject: 'bonjour', text: 'un message', question_type: 'procedure_info' },
InvisibleCaptcha.honeypots.sample => 'boom'
}
end
@ -156,15 +168,19 @@ describe SupportController, type: :controller do
describe 'send form' do
subject do
post :create, params: { helpscout_form: params }
post :create, params: { contact_form: params }
end
let(:params) { { subject: 'bonjour', email: "me@rspec.net", text: 'un message', type: 'procedure_info' } }
let(:params) { { subject: 'bonjour', email: "me@rspec.net", text: 'un message', question_type: 'procedure_info' } }
it 'creates a conversation on HelpScout' do
expect { subject }.to \
change(Commentaire, :count).by(0).and \
have_enqueued_job(HelpscoutCreateConversationJob).with(hash_including(params.except(:type)))
change(ContactForm, :count).by(1)
contact_form = ContactForm.last
expect(HelpscoutCreateConversationJob).to have_been_enqueued.with(contact_form)
expect(contact_form.email).to eq("me@rspec.net")
expect(flash[:notice]).to match('Votre message a été envoyé.')
expect(response).to redirect_to root_path
@ -184,39 +200,57 @@ describe SupportController, type: :controller do
end
context 'contact admin' do
subject do
post :create, params: { helpscout_form: params }
context 'index' do
it 'should have professionnal email field' do
get :admin
expect(response.body).to have_text('Votre adresse email professionnelle')
expect(response.body).to have_text('téléphone')
expect(response.body).to include('for_admin')
end
end
let(:params) { { for_admin: "true", email: "email@pro.fr", subject: 'bonjour', text: 'un message', type: 'admin question', phone: '06' } }
describe "when form is filled" do
it "creates a conversation on HelpScout" do
expect { subject }.to have_enqueued_job(HelpscoutCreateConversationJob).with(hash_including(params.except(:for_admin, :type)))
expect(flash[:notice]).to match('Votre message a été envoyé.')
context 'create' do
subject do
post :create, params: { contact_form: params }
end
context "with a piece justificative" do
let(:logo) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') }
let(:params) { super().merge(piece_jointe: logo) }
let(:params) { { for_admin: "true", email: "email@pro.fr", subject: 'bonjour', text: 'un message', question_type: 'admin question', phone: '06' } }
it "create blob and pass it to conversation job" do
expect { subject }.to \
change(ActiveStorage::Blob, :count).by(1).and \
have_enqueued_job(HelpscoutCreateConversationJob).with(hash_including(blob_id: Integer)).and \
have_enqueued_job(VirusScannerJob)
describe "when form is filled" do
it "creates a conversation on HelpScout" do
expect { subject }.to change(ContactForm, :count).by(1)
contact_form = ContactForm.last
expect(HelpscoutCreateConversationJob).to have_been_enqueued.with(contact_form)
expect(contact_form.email).to eq(params[:email])
expect(contact_form.phone).to eq("06")
expect(contact_form.tags).to match_array(["admin question", "contact form"])
expect(flash[:notice]).to match('Votre message a été envoyé.')
end
context "with a piece justificative" do
let(:logo) { fixture_file_upload('spec/fixtures/files/white.png', 'image/png') }
let(:params) { super().merge(piece_jointe: logo) }
it "create blob and pass it to conversation job" do
expect { subject }.to change(ContactForm, :count).by(1)
contact_form = ContactForm.last
expect(contact_form.piece_jointe).to be_attached
end
end
end
end
describe "when invisible captcha is filled" do
subject do
post :create, params: { helpscout_form: params, InvisibleCaptcha.honeypots.sample => 'boom' }
end
describe "when invisible captcha is filled" do
subject do
post :create, params: { contact_form: params, InvisibleCaptcha.honeypots.sample => 'boom' }
end
it 'does not create a conversation on HelpScout' do
subject
expect(flash[:alert]).to eq(I18n.t('invisible_captcha.sentence_for_humans'))
it 'does not create a conversation on HelpScout' do
subject
expect(flash[:alert]).to eq(I18n.t('invisible_captcha.sentence_for_humans'))
end
end
end
end

View file

@ -6,16 +6,9 @@ RSpec.describe HelpscoutCreateConversationJob, type: :job do
let(:subject_text) { 'Bonjour' }
let(:text) { "J'ai un pb" }
let(:tags) { ["first tag"] }
let(:question_type) { "lost" }
let(:phone) { nil }
let(:params) {
{
email:,
subject: subject_text,
text:,
tags:,
phone:
}
}
let(:contact_form) { create(:contact_form, email:, subject: subject_text, text:, tags:, phone:, question_type:) }
describe '#perform' do
before do
@ -26,56 +19,55 @@ RSpec.describe HelpscoutCreateConversationJob, type: :job do
headers: { 'Resource-ID' => 'new-conversation-id' }
))
allow(api).to receive(:add_tags)
allow(api).to receive(:add_phone_number) if params[:phone].present?
allow(api).to receive(:add_phone_number) if phone.present?
end
subject {
described_class.perform_now(**params)
described_class.perform_now(contact_form)
}
context 'when blob_id is not present' do
context 'when no file is attached' do
it 'sends the form without a file' do
subject
expect(api).to have_received(:create_conversation).with(email, subject_text, text, nil)
expect(api).to have_received(:add_tags).with("new-conversation-id", tags)
expect(api).to have_received(:add_tags).with("new-conversation-id", match_array(tags.concat(["contact form", question_type])))
expect(contact_form).to be_destroyed
end
end
context 'when blob_id is present' do
let(:blob) {
ActiveStorage::Blob.create_and_upload!(io: StringIO.new("toto"), filename: "toto.png")
}
let(:params) { super().merge(blob_id: blob.id) }
context 'when a file is attached' do
before do
allow(blob).to receive(:virus_scanner).and_return(double('VirusScanner', pending?: pending, safe?: safe))
allow(ActiveStorage::Blob).to receive(:find).with(blob.id).and_return(blob)
file = fixture_file_upload('spec/fixtures/files/white.png', 'image/png')
contact_form.piece_jointe.attach(file)
end
context 'when the file has not been scanned yet' do
let(:pending) { true }
let(:safe) { false }
before do
allow_any_instance_of(ActiveStorage::Blob).to receive(:virus_scanner).and_return(double('VirusScanner', pending?: true, safe?: false))
end
it 'reenqueue job' do
expect { subject }.to have_enqueued_job(described_class).with(params)
it 'reenqueues job' do
expect { subject }.to have_enqueued_job(described_class).with(contact_form)
end
end
context 'when the file is safe' do
let(:pending) { false }
let(:safe) { true }
before do
allow_any_instance_of(ActiveStorage::Blob).to receive(:virus_scanner).and_return(double('VirusScanner', pending?: false, safe?: true))
end
it 'downloads the file and sends the form' do
it 'sends the form with the file' do
subject
expect(api).to have_received(:create_conversation).with(email, subject_text, text, blob)
expect(api).to have_received(:create_conversation).with(email, subject_text, text, contact_form.piece_jointe)
end
end
context 'when the file is not safe' do
let(:pending) { false }
let(:safe) { false }
before do
allow_any_instance_of(ActiveStorage::Blob).to receive(:virus_scanner).and_return(double('VirusScanner', pending?: false, safe?: false))
end
it 'ignore the file' do
it 'ignores the file' do
subject
expect(api).to have_received(:create_conversation).with(email, subject_text, text, nil)
end
@ -84,6 +76,7 @@ RSpec.describe HelpscoutCreateConversationJob, type: :job do
context 'with a phone' do
let(:phone) { "06" }
it 'associates the phone number' do
subject
expect(api).to have_received(:add_phone_number).with(email, phone)