Merge pull request #7169 from tchak/fix-messagerie

Corrige l'exception lors de l'affichage de la messagerie
This commit is contained in:
Paul Chavard 2022-04-26 15:49:57 +02:00 committed by GitHub
commit 5007434ab0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 305 additions and 233 deletions

View file

@ -86,6 +86,7 @@ gem 'spreadsheet_architect'
gem 'strong_migrations' # lint database migrations
gem 'turbo-rails'
gem 'typhoeus'
gem 'view_component'
gem 'warden'
gem 'webpacker'
gem 'zipline'

View file

@ -741,6 +741,9 @@ GEM
activemodel (>= 3.0.0)
public_suffix
vcr (6.0.0)
view_component (2.53.0)
activesupport (>= 5.0.0, < 8.0)
method_source (~> 1.0)
virtus (2.0.0)
axiom-types (~> 0.1)
coercible (~> 1.0)
@ -904,6 +907,7 @@ DEPENDENCIES
turbo-rails
typhoeus
vcr
view_component
warden
web-console
webdrivers (~> 4.0)

View file

@ -0,0 +1,3 @@
class ApplicationComponent < ViewComponent::Base
include ViewComponent::Translatable
end

View file

@ -0,0 +1,58 @@
class Dossiers::MessageComponent < ApplicationComponent
def initialize(commentaire:, connected_user:, messagerie_seen_at: nil, show_reply_button: false)
@commentaire = commentaire
@connected_user = connected_user
@messagerie_seen_at = messagerie_seen_at
@show_reply_button = show_reply_button
end
attr_reader :commentaire, :connected_user, :messagerie_seen_at
private
def show_reply_button?
@show_reply_button
end
def highlight_if_unseen_class
helpers.highlight_if_unseen_class(@messagerie_seen_at, commentaire.created_at)
end
def icon_path
if commentaire.sent_by_system?
'icons/mail.svg'
elsif commentaire.sent_by?(connected_user)
'icons/account-circle.svg'
else
'icons/blue-person.svg'
end
end
def commentaire_issuer
if commentaire.sent_by_system?
t('.automatic_email')
elsif commentaire.sent_by?(connected_user)
t('.you')
else
commentaire.redacted_email
end
end
def commentaire_from_guest?
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
end
def commentaire_date
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
l(commentaire.created_at, format: is_current_year ? :message_date : :message_date_with_year)
end
def commentaire_body
if commentaire.discarded?
t('.deleted_body')
else
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
sanitize(body_formatted)
end
end
end

View file

@ -0,0 +1,9 @@
---
en:
reply: Reply
guest: Guest
delete_button: Delete this message
confirm: Are you sure you want to delete this message ?
automatic_email: Automatic email
you: You
deleted_body: Message deleted

View file

@ -0,0 +1,9 @@
---
fr:
reply: Répondre
guest: Invité
delete_button: Supprimer le message
confirm: Êtes-vous sûr de vouloir supprimer ce message ?
automatic_email: Email automatique
you: Vous
deleted_body: Message supprimé

View file

@ -0,0 +1,26 @@
= image_tag(icon_path, class: 'person-icon', alt: '')
.width-100
%h2
%span.mail
= commentaire_issuer
- if commentaire_from_guest?
%span.guest= t('.guest')
%span.date{ class: highlight_if_unseen_class }
= commentaire_date
.rich-text= commentaire_body
.message-extras.flex.justify-start
- if commentaire.soft_deletable?(connected_user)
= button_to instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire), method: :delete, class: 'button danger', form: { data: { turbo: true, turbo_confirm: t('.confirm') } } do
%span.icon.delete
= t('.delete_button')
- if commentaire.piece_jointe.attached?
.attachment-link
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
- if show_reply_button?
= button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do
%span.icon.reply
= t('.reply')

View file

@ -113,22 +113,6 @@ module Experts
end
end
def delete_commentaire
commentaire = avis.dossier.commentaires.find(params[:commentaire])
if commentaire.sent_by?(current_expert)
commentaire.piece_jointe.purge_later if commentaire.piece_jointe.attached?
commentaire.discard!
commentaire.update!(body: '')
flash[:notice] = t('views.shared.commentaires.destroy.notice')
else
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.acl')
end
redirect_to(messagerie_expert_avis_path(avis.procedure, avis))
rescue Discard::RecordNotDiscarded
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded')
redirect_to(messagerie_expert_avis_path(avis.procedure, avis))
end
def bilans_bdf
if avis.dossier.etablissement&.entreprise_bilans_bdf.present?
extension = params[:format]

View file

@ -1,21 +1,33 @@
# frozen_string_literal: true
module Instructeurs
class CommentairesController < ProceduresController
after_action :mark_messagerie_as_read
def destroy
commentaire = Dossier.find(params[:dossier_id]).commentaires.find(params[:id])
if commentaire.sent_by?(current_instructeur)
commentaire.piece_jointe.purge_later if commentaire.piece_jointe.attached?
commentaire.discard!
commentaire.update!(body: '')
flash[:notice] = t('views.shared.commentaires.destroy.notice')
if commentaire.sent_by?(current_instructeur) || commentaire.sent_by?(current_expert)
commentaire.soft_delete!
flash.notice = t('.notice')
else
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.acl')
flash.alert = t('.alert_acl')
end
redirect_to(messagerie_instructeur_dossier_path(params[:procedure_id], params[:dossier_id]))
rescue Discard::RecordNotDiscarded
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded')
redirect_to(messagerie_instructeur_dossier_path(params[:procedure_id], params[:dossier_id]))
# i18n-tasks-use t('instructeurs.commentaires.destroy.alert_already_discarded')
flash.alert = t('.alert_already_discarded')
end
private
def mark_messagerie_as_read
if commentaire.sent_by?(current_instructeur)
current_instructeur.mark_tab_as_seen(commentaire.dossier, :messagerie)
end
end
def commentaire
@commentaire ||= Dossier
.find(params[:dossier_id])
.commentaires
.find(params[:id])
end
end
end

View file

@ -12,20 +12,4 @@ module CommentaireHelper
I18n.t('helpers.commentaire.reply_in_mailbox')
end
end
def commentaire_is_from_guest(commentaire)
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
end
def commentaire_date(commentaire)
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
template = is_current_year ? :message_date : :message_date_with_year
I18n.l(commentaire.created_at, format: template)
end
def pretty_commentaire(commentaire)
return t('views.shared.commentaires.destroy.deleted_body') if commentaire.discarded?
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
sanitize(body_formatted)
end
end

View file

@ -10,8 +10,9 @@ import {
removeClass
} from '@utils';
const AUTOSAVE_DEBOUNCE_DELAY = gon.autosave.debounce_delay;
const AUTOSAVE_STATUS_VISIBLE_DURATION = gon.autosave.status_visible_duration;
const AUTOSAVE_DEBOUNCE_DELAY = window?.gon?.autosave?.debounce_delay;
const AUTOSAVE_STATUS_VISIBLE_DURATION =
window?.gon?.autosave?.status_visible_duration;
// Create a controller responsible for queuing autosave operations.
const autoSaveController = new AutoSaveController();

View file

@ -89,6 +89,12 @@ class Commentaire < ApplicationRecord
end
end
def soft_delete!
piece_jointe.purge_later if piece_jointe.attached?
discard!
update! body: ''
end
private
def notify

View file

@ -0,0 +1,3 @@
- if @commentaire.discarded?
= turbo_stream.update @commentaire do
= render Dossiers::MessageComponent.new(commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert)

View file

@ -49,7 +49,7 @@
%ul.messages-list
- @dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: current_instructeur, messagerie_seen_at: nil, show_reply_button: false }
= render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: current_instructeur)
%script{ type: "text/javascript" }
window.print();

View file

@ -0,0 +1,27 @@
!!! 5
%html{ lang: html_lang, class: yield(:root_class) }
%head
%meta{ "http-equiv": "Content-Type", content: "text/html; charset=UTF-8" }
%meta{ "http-equiv": "X-UA-Compatible", content: "IE=edge" }
%meta{ name: "viewport", content: "width=device-width, initial-scale=1" }
= csrf_meta_tags
%title
= content_for?(:title) ? "#{yield(:title)} · #{APPLICATION_NAME}" : APPLICATION_NAME
= favicon_link_tag(image_url("#{FAVICON_16PX_SRC}"), type: "image/png", sizes: "16x16")
= favicon_link_tag(image_url("#{FAVICON_32PX_SRC}"), type: "image/png", sizes: "32x32")
= favicon_link_tag(image_url("#{FAVICON_96PX_SRC}"), type: "image/png", sizes: "96x96")
= javascript_packs_with_chunks_tag 'application', defer: true
= preload_link_tag(asset_url("Muli-Regular.woff2"))
= preload_link_tag(asset_url("Muli-Bold.woff2"))
= stylesheet_link_tag 'application', media: 'all'
%body{ class: browser.platform.ios? ? 'ios' : nil }
.page-wrapper
%main.m-6
= content_for?(:content) ? yield(:content) : yield
%turbo-events

View file

@ -1,8 +1,8 @@
.messagerie.container
%ul.messages-list
- dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user) }
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user) }
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user), id: dom_id(commentaire) }
= render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user))
- if dossier.messagerie_available?
= render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url, dossier: dossier }

View file

@ -1,27 +0,0 @@
= render partial: 'shared/dossiers/messages/message_icon', locals: { commentaire: commentaire, connected_user: connected_user }
.width-100
%h2
%span.mail
= render partial: 'shared/dossiers/messages/message_issuer', locals: { commentaire: commentaire, connected_user: connected_user }
- if commentaire_is_from_guest(commentaire)
%span.guest= t('views.shared.dossiers.messages.message.guest')
%span.date{ class: highlight_if_unseen_class(messagerie_seen_at, commentaire.created_at) }
= commentaire_date(commentaire)
.rich-text= pretty_commentaire(commentaire)
.message-extras.flex.justify-start
- if commentaire.soft_deletable?(connected_user)
- path = connected_user.is_a?(Instructeur) ? instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) : delete_commentaire_expert_avis_path(@avis.procedure, @avis, commentaire: commentaire)
= button_to path, method: :delete, class: 'button danger', data: { confirm: t('views.shared.commentaires.destroy.confirm') } do
%span.icon.delete
= t('views.shared.commentaires.destroy.button')
- if commentaire.piece_jointe.attached?
.attachment-link
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
- if show_reply_button
= button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do
%span.icon.reply
= t('views.shared.dossiers.messages.message.reply')

View file

@ -1,7 +0,0 @@
- if commentaire.sent_by_system?
= image_tag('icons/mail.svg', class: 'person-icon', alt: '')
- elsif commentaire.sent_by?(connected_user)
= image_tag('icons/account-circle.svg', class: 'person-icon', alt: '')
- else
= image_tag('icons/blue-person.svg', class: 'person-icon', alt: '')

View file

@ -1,6 +0,0 @@
- if commentaire.sent_by_system?
= t('views.shared.dossiers.messages.message_issuer.automatic_email')
- elsif commentaire.sent_by?(connected_user)
= t('views.shared.dossiers.messages.message_issuer.you')
- else
= commentaire.redacted_email

View file

@ -4,7 +4,7 @@
%h3.tab-title= t('views.users.dossiers.show.latest_message.latest_message')
.message.inverted-background
= render partial: "shared/dossiers/messages/message", locals: { commentaire: latest_message, connected_user: current_user, messagerie_seen_at: nil, show_reply_button: false }
= render Dossiers::MessageComponent.new(commentaire: latest_message, connected_user: current_user)
= link_to messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button send' do
%span.icon.reply

View file

@ -81,5 +81,13 @@ module TPS
# Custom Configuration
# @see https://guides.rubyonrails.org/configuring.html#custom-configuration
config.x.clamav.enabled = ENV.fetch("CLAMAV_ENABLED", "enabled") == "enabled"
config.view_component.generate_sidecar = true
config.view_component.generate_locale = true
config.view_component.generate_distinct_locale_files = true
config.view_component.generate_preview = true
config.view_component.show_previews_source = true
config.view_component.default_preview_layout = 'component_preview'
config.view_component.preview_paths << "#{Rails.root}/spec/components/previews"
end
end

View file

@ -68,6 +68,7 @@ search:
- app/assets/images
- app/assets/fonts
- app/assets/videos
- app/components
## Alternatively, the only files or `File.fnmatch patterns` to search in `paths`:
## If specified, this settings takes priority over `exclude`, but `exclude` still applies.

View file

@ -127,12 +127,6 @@ en:
submit_dossier: Submit the file
save_changes: Save the changes of the file
messages:
message_issuer:
automatic_email: "Automatic email"
you: "You"
message:
reply: "Reply"
guest: "Guest"
form:
send_message: "Send message"
attachment_size: "(attachment size max : 20 Mo)"

View file

@ -122,12 +122,6 @@ fr:
submit_dossier: Déposer le dossier
save_changes: Enregistrer les modifications du dossier
messages:
message_issuer:
automatic_email: "Email automatique"
you: "Vous"
message:
reply: "Répondre"
guest: "Invité"
form:
send_message: "Envoyer le message"
attachment_size: "(taille max : 20 Mo)"

View file

@ -0,0 +1,7 @@
en:
instructeurs:
commentaires:
destroy:
notice: Your message had been deleted
alert_acl: "Can not destroy message: it does not belong to you"
alert_already_discarded: "Can not destroy message: it was already destroyed"

View file

@ -0,0 +1,7 @@
fr:
instructeurs:
commentaires:
destroy:
notice: Votre message a été supprimé
alert_acl: Impossible de supprimer le message, celui ci ne vous appartient pas
alert_already_discarded: Ce message a déjà été supprimé

View file

@ -2,4 +2,4 @@ en:
instructeurs:
procedure:
archive_pending_html: Archive creation pending<br>(requested %{created_period} ago)
archive_ready_html: Download archive<br>(requested %{generated_period} ago)
archive_ready_html: Download archive<br>(requested %{generated_period} ago)

View file

@ -20,12 +20,3 @@ en:
already_user: "I already have an account"
create: 'Create an account'
signin: 'Sign in'
commentaires:
destroy:
button: 'Destroy this message'
confirm: "Are you sure you want to destroy this message ?"
deleted_body: Message deleted
notice: 'Your message had been deleted'
alert_reasons:
acl: "Can not destroy message: it does not belong to you"
already_discarded: "Can not destroy message: it was already destroyed"

View file

@ -20,12 +20,3 @@ fr:
already_user: 'Jai déjà un compte'
create: 'Créer un compte'
signin: 'Connexion'
commentaires:
destroy:
button: 'Supprimer le message'
confirm: "Êtes-vous sûr de vouloir supprimer ce message ?"
deleted_body: Message supprimé
notice: 'Votre message a été supprimé'
alert_reasons:
acl: "Impossible de supprimer le message, celui ci ne vous appartient pas"
already_discarded: "Ce message a déjà été supprimé"

View file

@ -310,7 +310,6 @@ Rails.application.routes.draw do
get 'instruction'
get 'messagerie'
post 'commentaire' => 'avis#create_commentaire'
delete 'delete_commentaire' => 'avis#delete_commentaire'
post 'avis' => 'avis#create_avis'
get 'bilans_bdf'
get 'telecharger_pjs' => 'avis#telecharger_pjs'

View file

@ -1,21 +1,28 @@
describe 'shared/dossiers/messages/message.html.haml', type: :view do
before { view.extend DossierHelper }
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: dossier.user, show_reply_button: true }
RSpec.describe Dossiers::MessageComponent, type: :component do
let(:component) do
described_class.new(
commentaire: commentaire,
connected_user: connected_user,
messagerie_seen_at: seen_at,
show_reply_button: true
)
end
let(:dossier) { create(:dossier, :en_construction) }
let(:commentaire) { create(:commentaire, dossier: dossier) }
let(:connected_user) { dossier.user }
let(:seen_at) { commentaire.created_at + 1.hour }
subject { render_inline(component).to_html }
it { is_expected.to have_button("Répondre") }
context "with a seen_at after commentaire created_at" do
context 'with a seen_at after commentaire created_at' do
let(:seen_at) { commentaire.created_at + 1.hour }
it { is_expected.not_to have_css(".highlighted") }
end
context "with a seen_at after commentaire created_at" do
context 'with a seen_at after commentaire created_at' do
let(:seen_at) { commentaire.created_at - 1.hour }
it { is_expected.to have_css(".highlighted") }
@ -51,8 +58,8 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) }
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: instructeur, show_reply_button: true }
let(:form_url) { instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) }
let(:connected_user) { instructeur }
let(:form_url) { component.helpers.instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) }
context 'on a procedure where commentaire had been written by connected instructeur' do
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message') }
@ -64,7 +71,7 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message', discarded_at: 2.days.ago) }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
it { is_expected.not_to have_selector(".rich-text", text: I18n.t(t('views.shared.commentaires.destroy.deleted_body'))) }
it { is_expected.to have_selector(".rich-text", text: component.t('.deleted_body')) }
end
context 'on a procedure where commentaire had been written by connected an user' do
@ -87,49 +94,48 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
end
end
context 'with an expert message' do
describe 'delete message button for expert' do
let(:expert) { create(:expert) }
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) }
let(:experts_procedure) { create(:experts_procedure, procedure: procedure, expert: expert) }
let!(:avis) { create(:avis, email: nil, experts_procedure: experts_procedure) }
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: expert, show_reply_button: true }
let(:form_url) { delete_commentaire_expert_avis_path(avis.procedure, avis, commentaire: commentaire) }
describe '#commentaire_from_guest?' do
let!(:guest) { create(:invite, dossier: dossier) }
before do
assign(:avis, avis)
subject { component.send(:commentaire_from_guest?) }
context 'when the commentaire sender is not a guest' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
it { is_expected.to be false }
end
context 'when the commentaire sender is a guest on this dossier' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
it { is_expected.to be true }
end
end
describe '#commentaire_date' do
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
let(:creation_date) { present_date }
let(:commentaire) do
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
end
subject do
Timecop.freeze(present_date) { component.send(:commentaire_date) }
end
it 'doesnt include the creation year' do
expect(subject).to eq 'le 2 septembre à 10 h 05'
end
context 'when displaying a commentaire created on a previous year' do
let(:creation_date) { present_date.prev_year }
it 'includes the creation year' do
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
end
end
context 'on a procedure where commentaire had been written by connected expert' do
let(:commentaire) { create(:commentaire, expert: expert, body: 'Second message') }
it { is_expected.to have_selector("form[action=\"#{form_url}\"]") }
end
context 'on a procedure where commentaire had been written by connected expert and discarded' do
let(:commentaire) { create(:commentaire, expert: expert, body: 'Second message', discarded_at: 2.days.ago) }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
it { is_expected.not_to have_selector(".rich-text", text: I18n.t(t('views.shared.commentaires.destroy.deleted_body'))) }
end
context 'on a procedure where commentaire had been written by connected an user' do
let(:commentaire) { create(:commentaire, email: create(:user).email, body: 'Second message') }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
end
context 'on a procedure where commentaire had been written by connected an instructeur' do
let(:commentaire) { create(:commentaire, instructeur: create(:instructeur), body: 'Second message') }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
end
context 'on a procedure where commentaire had been written another expert' do
let(:commentaire) { create(:commentaire, expert: create(:expert), body: 'Second message') }
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
context 'when formatting the first day of the month' do
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
it 'includes the ordinal' do
expect(subject).to eq 'le 1er septembre à 10 h 05'
end
end
end

View file

@ -0,0 +1,31 @@
class Dossiers::MessageComponentPreview < ViewComponent::Preview
def with_default_commentaire
render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: user)
end
def with_discarded_commentaire
render Dossiers::MessageComponent.new(commentaire: discarded_commentaire, connected_user: user)
end
private
def user
User.new email: "usager@example.com", locale: I18n.locale
end
def commentaire
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago
end
def discarded_commentaire
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago, discarded_at: 1.day.ago
end
def dossier
Dossier.new(id: 47882, state: :en_instruction, procedure: procedure, user: user)
end
def procedure
Procedure.new id: 1234, libelle: 'Dotation dÉquipement des Territoires Ruraux - Exercice 2019'
end
end

View file

@ -4,33 +4,33 @@ describe Instructeurs::CommentairesController, type: :controller do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, :for_individual, instructeurs: [instructeur]) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
render_views
before { sign_in(instructeur.user) }
describe 'destroy' do
render_views
context 'when it works' do
let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier) }
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id } }
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id }, format: :turbo_stream }
it 'redirect to dossier' do
expect(subject).to redirect_to(messagerie_instructeur_dossier_path(dossier.procedure, dossier))
end
it 'flash success' do
subject
expect(flash[:notice]).to eq(I18n.t('views.shared.commentaires.destroy.notice'))
it 'respond with OK and flash' do
expect(subject).to have_http_status(:ok)
expect(subject.body).to include('Message supprimé')
expect(subject.body).to include('alert-success')
expect(subject.body).to include('Votre message a été supprimé')
end
end
context 'when dossier had been discarded' do
let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier, discarded_at: 2.hours.ago) }
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id } }
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id }, format: :turbo_stream }
it 'redirect to dossier' do
expect(subject).to redirect_to(messagerie_instructeur_dossier_path(dossier.procedure, dossier))
end
it 'flash success' do
subject
expect(flash[:alert]).to eq(I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded'))
it 'respond with OK and flash' do
expect(subject).to have_http_status(:ok)
expect(subject.body).to include('alert-danger')
expect(subject.body).to include('Ce message a déjà été supprimé')
end
end
end

View file

@ -28,51 +28,4 @@ RSpec.describe CommentaireHelper, type: :helper do
it { is_expected.to include('Répondre') }
end
end
describe '.commentaire_is_from_guest' do
let(:dossier) { create(:dossier, :en_instruction) }
let!(:guest) { create(:invite, dossier: dossier) }
subject { commentaire_is_from_guest(commentaire) }
context 'when the commentaire sender is not a guest' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
it { is_expected.to be false }
end
context 'when the commentaire sender is a guest on this dossier' do
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
it { is_expected.to be true }
end
end
describe '.commentaire_date' do
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
let(:creation_date) { present_date }
let(:commentaire) do
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
end
subject do
Timecop.freeze(present_date) { commentaire_date(commentaire) }
end
it 'doesnt include the creation year' do
expect(subject).to eq 'le 2 septembre à 10 h 05'
end
context 'when displaying a commentaire created on a previous year' do
let(:creation_date) { present_date.prev_year }
it 'includes the creation year' do
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
end
end
context 'when formatting the first day of the month' do
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
it 'includes the ordinal' do
expect(subject).to eq 'le 1er septembre à 10 h 05'
end
end
end
end

View file

@ -13,6 +13,7 @@ require 'rspec/rails'
require 'axe-rspec'
require 'devise'
require 'shoulda-matchers'
require 'view_component/test_helpers'
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@ -124,4 +125,6 @@ RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :system
config.include ViewComponent::TestHelpers, type: :component
config.include Capybara::RSpecMatchers, type: :component
end