diff --git a/Gemfile.lock b/Gemfile.lock index cf449d922..41c577d6b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -74,8 +74,8 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) administrate (0.11.0) actionpack (>= 4.2, < 6.0) actionview (>= 4.2, < 6.0) @@ -119,18 +119,18 @@ GEM browser (2.5.3) builder (3.2.3) byebug (10.0.2) - capybara (3.12.0) + capybara (3.29.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.2) + regexp_parser (~> 1.5) xpath (~> 3.2) capybara-email (3.0.1) capybara (>= 2.4, < 4.0) mail - capybara-screenshot (1.0.22) + capybara-screenshot (1.0.23) capybara (>= 1.0, < 4) launchy capybara-selenium (0.0.6) @@ -373,7 +373,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) mimemagic (0.3.3) - mini_mime (1.0.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.11.3) momentjs-rails (2.20.1) @@ -448,7 +448,7 @@ GEM pry-byebug (3.6.0) byebug (~> 10.0) pry (~> 0.10) - public_suffix (3.0.3) + public_suffix (4.0.1) puma (3.12.0) pundit (2.0.1) activesupport (>= 3.0.0) @@ -512,7 +512,7 @@ GEM execjs railties (>= 3.2) tilt - regexp_parser (1.3.0) + regexp_parser (1.6.0) request_store (1.4.1) rack (>= 1.4) responders (3.0.0) diff --git a/app/assets/stylesheets/new_design/groupe_instructeur.scss b/app/assets/stylesheets/new_design/groupe_instructeur.scss new file mode 100644 index 000000000..419bf075f --- /dev/null +++ b/app/assets/stylesheets/new_design/groupe_instructeur.scss @@ -0,0 +1,6 @@ +.groupe-instructeur { + .actions { + width: 200px; + text-align: center; + } +} diff --git a/app/assets/stylesheets/new_design/utils.scss b/app/assets/stylesheets/new_design/utils.scss index ef32f9f20..9e5b14290 100644 --- a/app/assets/stylesheets/new_design/utils.scss +++ b/app/assets/stylesheets/new_design/utils.scss @@ -1,4 +1,5 @@ @import "colors"; +@import "constants"; .pull-left { float: left; @@ -48,3 +49,7 @@ background: $orange-bg; color: $black; } + +.mt-2 { + margin-top: 2 * $default-spacer; +} diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 9b76145ff..a30a52f7e 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -264,7 +264,7 @@ module Instructeurs end def ensure_ownership! - if !procedure.defaut_groupe_instructeur.instructeurs.include?(current_instructeur) + if !current_instructeur.procedures.include?(procedure) flash[:alert] = "Vous n'avez pas accès à cette démarche" redirect_to root_path end diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb new file mode 100644 index 000000000..b8923d4a3 --- /dev/null +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -0,0 +1,145 @@ +module NewAdministrateur + class GroupeInstructeursController < AdministrateurController + ITEMS_PER_PAGE = 25 + + def index + @procedure = procedure + + @groupes_instructeurs = paginated_groupe_instructeurs + end + + def show + @procedure = procedure + @groupe_instructeur = groupe_instructeur + @instructeurs = paginated_instructeurs + end + + def create + @groupe_instructeur = procedure + .groupe_instructeurs + .new(label: label, instructeurs: [current_administrateur.instructeur]) + + if @groupe_instructeur.save + redirect_to procedure_groupe_instructeur_path(procedure, @groupe_instructeur), + notice: "Le groupe d’instructeurs « #{label} » a été créé." + else + @procedure = procedure + @groupes_instructeurs = paginated_groupe_instructeurs + + flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe." + render :index + end + end + + def update + @groupe_instructeur = groupe_instructeur + + if @groupe_instructeur.update(label: label) + redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur), + notice: "Le nom est à présent « #{label} »." + else + @procedure = procedure + @instructeurs = paginated_instructeurs + + flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe." + render :show + end + end + + def add_instructeur + @instructeur = Instructeur.find_by(email: instructeur_email) || + create_instructeur(instructeur_email) + + if groupe_instructeur.instructeurs.include?(@instructeur) + flash[:alert] = "L’instructeur « #{instructeur_email} » est déjà dans le groupe." + + else + groupe_instructeur.instructeurs << @instructeur + flash[:notice] = "L’instructeur « #{instructeur_email} » a été affecté au groupe." + GroupeInstructeurMailer + .add_instructeur(groupe_instructeur, @instructeur, current_user.email) + .deliver_later + end + + redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur) + end + + def remove_instructeur + if groupe_instructeur.instructeurs.one? + flash[:alert] = "Suppression impossible : il doit y avoir au moins un instructeur dans le groupe" + + else + @instructeur = Instructeur.find(instructeur_id) + groupe_instructeur.instructeurs.destroy(@instructeur) + flash[:notice] = "L’instructeur « #{@instructeur.email} » a été retiré du groupe." + GroupeInstructeurMailer + .remove_instructeur(groupe_instructeur, @instructeur, current_user.email) + .deliver_later + end + + redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur) + end + + def update_routing_criteria_name + procedure.update!(routing_criteria_name: routing_criteria_name) + + redirect_to procedure_groupe_instructeurs_path(procedure), + notice: "Le libellé est maintenant « #{procedure.routing_criteria_name} »." + end + + private + + def create_instructeur(email) + user = User.create_or_promote_to_instructeur( + email, + SecureRandom.hex, + administrateurs: [current_administrateur] + ) + user.invite! + user.instructeur + end + + def procedure + current_administrateur + .procedures + .includes(:groupe_instructeurs) + .find(params[:procedure_id]) + end + + def groupe_instructeur + procedure.groupe_instructeurs.find(params[:id]) + end + + def instructeur_email + params[:instructeur][:email].strip.downcase + end + + def instructeur_id + params[:instructeur][:id] + end + + def label + params[:groupe_instructeur][:label] + end + + def paginated_groupe_instructeurs + procedure + .groupe_instructeurs + .page(params[:page]) + .per(ITEMS_PER_PAGE) + .order(:label) + end + + def paginated_instructeurs + groupe_instructeur + .instructeurs + .page(params[:page]) + .per(ITEMS_PER_PAGE) + .order(:email) + end + + def routing_criteria_name + params[:procedure][:routing_criteria_name] + end + end +end diff --git a/app/helpers/dossier_link_helper.rb b/app/helpers/dossier_link_helper.rb index 1a3986771..93c9d9c16 100644 --- a/app/helpers/dossier_link_helper.rb +++ b/app/helpers/dossier_link_helper.rb @@ -1,7 +1,7 @@ module DossierLinkHelper def dossier_linked_path(user, dossier) if user.is_a?(Instructeur) - if dossier.procedure.defaut_groupe_instructeur.instructeurs.include?(user) + if user.groupe_instructeurs.include?(dossier.groupe_instructeur) instructeur_dossier_path(dossier.procedure, dossier) else avis = dossier.avis.find_by(instructeur: user) diff --git a/app/mailers/groupe_instructeur_mailer.rb b/app/mailers/groupe_instructeur_mailer.rb new file mode 100644 index 000000000..f5af9dd3c --- /dev/null +++ b/app/mailers/groupe_instructeur_mailer.rb @@ -0,0 +1,25 @@ +class GroupeInstructeurMailer < ApplicationMailer + layout 'mailers/layout' + + def add_instructeur(group, instructeur, current_instructeur_email) + @email = instructeur.email + @group = group + @current_instructeur_email = current_instructeur_email + + subject = "Ajout d’un instructeur dans le groupe \"#{group.label}\"" + + emails = @group.instructeurs.pluck(:email) + mail(bcc: emails, subject: subject) + end + + def remove_instructeur(group, instructeur, current_instructeur_email) + @email = instructeur.email + @group = group + @current_instructeur_email = current_instructeur_email + + subject = "Suppression d’un instructeur dans le groupe \"#{group.label}\"" + + emails = @group.instructeurs.pluck(:email) + mail(bcc: emails, subject: subject) + end +end diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index e3202bdbd..8ebd1dd27 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -4,4 +4,9 @@ class GroupeInstructeur < ApplicationRecord has_many :assign_tos has_many :instructeurs, through: :assign_tos, dependent: :destroy has_many :dossiers + + validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false + validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' } + + before_validation -> { label&.strip! } end diff --git a/app/views/groupe_instructeur_mailer/add_instructeur.html.haml b/app/views/groupe_instructeur_mailer/add_instructeur.html.haml new file mode 100644 index 000000000..578312cb7 --- /dev/null +++ b/app/views/groupe_instructeur_mailer/add_instructeur.html.haml @@ -0,0 +1,11 @@ +%p + Bonjour, + +%p + L’instructeur « #{@email} » a été affecté au groupe « #{@group.label} » par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ». + +%p + Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe : + = link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group)) + += render partial: "layouts/mailers/signature" diff --git a/app/views/groupe_instructeur_mailer/remove_instructeur.html.haml b/app/views/groupe_instructeur_mailer/remove_instructeur.html.haml new file mode 100644 index 000000000..15a3f15a7 --- /dev/null +++ b/app/views/groupe_instructeur_mailer/remove_instructeur.html.haml @@ -0,0 +1,11 @@ +%p + Bonjour, + +%p + L’instructeur « #{@email} » a été retiré du groupe « #{@group.label} » par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ». + +%p + Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe : + = link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group)) + += render partial: "layouts/mailers/signature" diff --git a/app/views/instructeurs/recherche/index.html.haml b/app/views/instructeurs/recherche/index.html.haml index 2ccce0994..635895a60 100644 --- a/app/views/instructeurs/recherche/index.html.haml +++ b/app/views/instructeurs/recherche/index.html.haml @@ -3,7 +3,7 @@ .container .page-title Résultat de la recherche : - = pluralize(@dossiers.count, "dossier trouvé", "dossiers trouvés") + = t('pluralize.dossier_trouve', count: @dossiers.count) - if @dossiers.present? %table.table.dossiers-table.hoverable diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 4429e2052..4f0284677 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -27,14 +27,20 @@ %p.missing-steps (à compléter) %a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) } - .procedure-list-element{ class: ('active' if active == 'Administrateurs') } + .procedure-list-element Administrateurs - %a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) } - .procedure-list-element{ class: ('active' if active == 'Instructeurs') } - Instructeurs - - if @procedure.missing_steps.include?(:instructeurs) - %p.missing-steps (à compléter) + - if !feature_enabled?(:routage) + %a#onglet-instructeurs{ href: url_for(admin_procedure_assigns_path(@procedure)) } + .procedure-list-element{ class: ('active' if active == 'Instructeurs') } + Instructeurs + - if @procedure.missing_steps.include?(:instructeurs) + %p.missing-steps (à compléter) + + - if feature_enabled?(:routage) + %a#onglet-instructeurs{ href: url_for(procedure_groupe_instructeurs_path(@procedure)) } + .procedure-list-element + Groupe d'instructeurs - if !@procedure.locked? %a#onglet-champs{ href: champs_procedure_path(@procedure) } diff --git a/app/views/new_administrateur/groupe_instructeurs/index.html.haml b/app/views/new_administrateur/groupe_instructeurs/index.html.haml new file mode 100644 index 000000000..6f9661a11 --- /dev/null +++ b/app/views/new_administrateur/groupe_instructeurs/index.html.haml @@ -0,0 +1,38 @@ += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to('Démarches', admin_procedures_path), + link_to(@procedure.libelle, admin_procedure_path(@procedure)), + 'Groupes d’instructeurs'] } + +.container.groupe-instructeur + .card + = form_for @procedure, + url: { action: :update_routing_criteria_name }, + html: { class: 'form' } do |f| + + = f.label :routing_criteria_name do + Libellé du routage + %span.notice Ce texte apparaitra sur le formulaire usager comme le libellé d'une liste + = f.text_field :routing_criteria_name, placeholder: 'Votre ville', required: true + = f.submit 'Renommer', class: 'button primary send' + + .card + .card-title Gestion des Groupes + + = form_for :groupe_instructeur, html: { class: 'form' } do |f| + = f.label :label do + Ajouter un groupe + %span.notice Ce groupe sera un choix de la liste « #{@procedure.routing_criteria_name} » . + = f.text_field :label, placeholder: 'Ville de Bordeaux', required: true + = f.submit 'Ajouter le groupe', class: 'button primary send' + + %table.table.mt-2 + %thead + %tr + %th{ colspan: 2 } Liste des groupes + %tbody + - @groupes_instructeurs.each do |group| + %tr + %td= group.label + %td.actions= link_to "voir", procedure_groupe_instructeur_path(@procedure, group) + + = paginate @groupes_instructeurs diff --git a/app/views/new_administrateur/groupe_instructeurs/show.html.haml b/app/views/new_administrateur/groupe_instructeurs/show.html.haml new file mode 100644 index 000000000..40a3df3f4 --- /dev/null +++ b/app/views/new_administrateur/groupe_instructeurs/show.html.haml @@ -0,0 +1,47 @@ += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to('Démarches', admin_procedures_path), + link_to(@procedure.libelle, admin_procedure_path(@procedure)), + link_to('Groupes d’instructeurs', procedure_groupe_instructeurs_path(@procedure)), + @groupe_instructeur.label] } + +.container.groupe-instructeur + .rename_form_block + .flex.baseline-start + %h1 Groupe « #{@groupe_instructeur.label} » + + .card.mt-2 + = form_for @groupe_instructeur, + url: procedure_groupe_instructeur_path(@procedure, @groupe_instructeur), + html: { class: 'form' } do |f| + + = f.label :label, 'Nom du groupe' + = f.text_field :label, placeholder: 'Ville de Bordeaux', required: true + = f.submit 'Renommer', class: 'button primary send' + + .card + .card-title Gestion des instructeurs + = form_for :instructeur, + url: { action: :add_instructeur }, + html: { class: 'form' } do |f| + + = f.label :email do + Affecter un nouvel instructeur + = f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true + = f.submit 'Affecter', class: 'button primary send' + + %table.table.mt-2 + %thead + %tr + %th{ colspan: 2 } Instructeurs affectés + %tbody + - @instructeurs.each do |instructeur| + %tr + %td= instructeur.email + %td.actions= button_to 'retirer', + { action: :remove_instructeur }, + { method: :delete, + data: { confirm: "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » du groupe  « #{@groupe_instructeur.label} » ?" }, + params: { instructeur: { id: instructeur.id }}, + class: 'button' } + + = paginate @instructeurs diff --git a/config/locales/fr.yml b/config/locales/fr.yml index c44b18967..5935c6cc9 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -313,3 +313,7 @@ fr: zero: archivé one: archivé other: archivés + dossier_trouve: + zero: 0 dossier trouvé + one: 1 dossier trouvé + other: "%{count} dossiers trouvés" diff --git a/config/routes.rb b/config/routes.rb index d60cd0c65..5730cf8ef 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -350,6 +350,17 @@ Rails.application.routes.draw do get 'annotations' end + resources :groupe_instructeurs, only: [:index, :show, :create, :update] do + member do + post 'add_instructeur' + delete 'remove_instructeur' + end + + collection do + patch 'update_routing_criteria_name' + end + end + resources :administrateurs, controller: 'procedure_administrateurs', only: [:index, :create, :destroy] resources :types_de_champ, only: [:create, :update, :destroy] do diff --git a/db/migrate/20191023183120_add_default_value_to_routing_criteria_name.rb b/db/migrate/20191023183120_add_default_value_to_routing_criteria_name.rb new file mode 100644 index 000000000..d9cd9fff7 --- /dev/null +++ b/db/migrate/20191023183120_add_default_value_to_routing_criteria_name.rb @@ -0,0 +1,5 @@ +class AddDefaultValueToRoutingCriteriaName < ActiveRecord::Migration[5.2] + def change + change_column :procedures, :routing_criteria_name, :text, default: "Votre ville" + end +end diff --git a/db/schema.rb b/db/schema.rb index 3736ce621..b4c4e4823 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_10_14_160538) do +ActiveRecord::Schema.define(version: 2019_10_23_183120) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -486,7 +486,7 @@ ActiveRecord::Schema.define(version: 2019_10_14_160538) do t.string "path", null: false t.string "declarative_with_state" t.text "monavis_embed" - t.text "routing_criteria_name" + t.text "routing_criteria_name", default: "Votre ville" t.boolean "csv_export_queued" t.boolean "xlsx_export_queued" t.boolean "ods_export_queued" diff --git a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb new file mode 100644 index 000000000..6b0d388a4 --- /dev/null +++ b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb @@ -0,0 +1,162 @@ +describe NewAdministrateur::GroupeInstructeursController, type: :controller do + render_views + + let(:admin) { create(:administrateur) } + let(:procedure) { create(:procedure, :published, administrateurs: [admin]) } + let!(:gi_1_1) { procedure.defaut_groupe_instructeur } + + let(:procedure2) { create(:procedure, :published) } + let!(:gi_2_2) { procedure2.groupe_instructeurs.create(label: 'groupe instructeur 2 2') } + + before { sign_in(admin.user) } + + describe '#index' do + context 'of a procedure I own' do + let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + + before { get :index, params: { procedure_id: procedure.id } } + + context 'when a procedure has multiple groups' do + it { expect(response).to have_http_status(:ok) } + it { expect(response.body).to include(gi_1_1.label) } + it { expect(response.body).to include(gi_1_2.label) } + it { expect(response.body).not_to include(gi_2_2.label) } + end + end + end + + describe '#show' do + context 'of a group I belong to' do + before { get :show, params: { procedure_id: procedure.id, id: gi_1_1.id } } + + it { expect(response).to have_http_status(:ok) } + end + end + + describe '#create' do + before do + post :create, + params: { + procedure_id: procedure.id, + groupe_instructeur: { label: label } + } + end + + context 'with a valid name' do + let(:label) { "nouveau_groupe" } + + it { expect(flash.notice).to be_present } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.groupe_instructeurs.last)) } + it { expect(procedure.groupe_instructeurs.count).to eq(2) } + end + + context 'with an invalid group name' do + let(:label) { gi_1_1.label } + + it { expect(response).to render_template(:index) } + it { expect(procedure.groupe_instructeurs.count).to eq(1) } + it { expect(flash.alert).to be_present } + end + end + + describe '#update' do + let(:new_name) { 'nouveau nom du groupe' } + + before do + patch :update, + params: { + procedure_id: procedure.id, + id: gi_1_1.id, + groupe_instructeur: { label: new_name } + } + end + + it { expect(gi_1_1.reload.label).to eq(new_name) } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) } + it { expect(flash.notice).to be_present } + + context 'when the name is already taken' do + let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + let(:new_name) { gi_1_2.label } + + it { expect(gi_1_1.reload.label).not_to eq(new_name) } + it { expect(flash.alert).to be_present } + end + end + + describe '#add_instructeur' do + let!(:instructeur) { create(:instructeur) } + before do + gi_1_1.instructeurs << instructeur + + post :add_instructeur, + params: { + procedure_id: procedure.id, + id: gi_1_1.id, + instructeur: { email: new_instructeur_email } + } + end + + context 'of a new instructeur' do + let(:new_instructeur_email) { 'new_instructeur@mail.com' } + + it { expect(gi_1_1.instructeurs.pluck(:email)).to include(new_instructeur_email) } + it { expect(flash.notice).to be_present } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) } + end + + context 'of an instructeur already in the group' do + let(:new_instructeur_email) { instructeur.email } + + it { expect(flash.alert).to be_present } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur)) } + end + end + + describe '#remove_instructeur' do + let!(:instructeur) { create(:instructeur) } + + before { gi_1_1.instructeurs << admin.instructeur << instructeur } + + def remove_instructeur(email) + delete :remove_instructeur, + params: { + procedure_id: procedure.id, + id: gi_1_1.id, + instructeur: { id: admin.instructeur.id } + } + end + + context 'when there are many instructeurs' do + before { remove_instructeur(admin.user.email) } + + it { expect(gi_1_1.instructeurs).to include(instructeur) } + it { expect(gi_1_1.reload.instructeurs.count).to eq(1) } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) } + end + + context 'when there is only one instructeur' do + before do + remove_instructeur(admin.user.email) + remove_instructeur(instructeur.email) + end + + it { expect(gi_1_1.instructeurs).to include(instructeur) } + it { expect(gi_1_1.instructeurs.count).to eq(1) } + it { expect(flash.alert).to eq('Suppression impossible : il doit y avoir au moins un instructeur dans le groupe') } + it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) } + end + end + + describe '#update_routing_criteria_name' do + before do + patch :update_routing_criteria_name, + params: { + procedure_id: procedure.id, + procedure: { routing_criteria_name: 'new name !' } + } + end + + it { expect(procedure.reload.routing_criteria_name).to eq('new name !') } + end +end diff --git a/spec/features/routing/full_scenario_spec.rb b/spec/features/routing/full_scenario_spec.rb new file mode 100644 index 000000000..86da5f97c --- /dev/null +++ b/spec/features/routing/full_scenario_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' + +feature 'The routing' do + let(:procedure) { create(:procedure, :with_service, :for_individual) } + let(:administrateur) { create(:administrateur, procedures: [procedure]) } + let(:scientifique_user) { create(:user) } + let(:litteraire_user) { create(:user) } + + before { Flipper.enable_actor(:routage, administrateur.user) } + + scenario 'works' do + login_as administrateur.user, scope: :user + + visit admin_procedure_path(procedure.id) + click_on "Groupe d'instructeurs" + + # rename routing criteria to spécialité + fill_in 'procedure_routing_criteria_name', with: 'spécialité' + click_on 'Renommer' + expect(procedure.reload.routing_criteria_name).to eq('spécialité') + + # rename defaut groupe to littéraire + click_on 'voir' + fill_in 'groupe_instructeur_label', with: 'littéraire' + click_on 'Renommer' + + # add victor to littéraire groupe + fill_in 'instructeur_email', with: 'victor@inst.com' + perform_enqueued_jobs { click_on 'Affecter' } + victor = User.find_by(email: 'victor@inst.com').instructeur + + click_on "Groupes d’instructeurs" + + # add scientifique groupe + fill_in 'groupe_instructeur_label', with: 'scientifique' + click_on 'Ajouter le groupe' + + # add marie to scientifique groupe + fill_in 'instructeur_email', with: 'marie@inst.com' + perform_enqueued_jobs { click_on 'Affecter' } + marie = User.find_by(email: 'marie@inst.com').instructeur + + # publish + publish_procedure(procedure) + log_out + + # 2 users fill a dossier in each group + user_send_dossier(scientifique_user, 'scientifique') + user_send_dossier(litteraire_user, 'littéraire') + + # the litteraires instructeurs only manage the litteraires dossiers + register_instructeur_and_log_in(victor.email) + click_on procedure.libelle + expect(page).to have_text(litteraire_user.email) + expect(page).not_to have_text(scientifique_user.email) + + # the search only show litteraires dossiers + fill_in 'q', with: scientifique_user.email + click_on 'Rechercher' + expect(page).to have_text('0 dossier trouvé') + + fill_in 'q', with: litteraire_user.email + click_on 'Rechercher' + expect(page).to have_text('1 dossier trouvé') + + ## and the result is clickable + click_on litteraire_user.email + expect(page).to have_current_path(instructeur_dossier_path(procedure, litteraire_user.dossiers.first)) + + log_out + + # the scientifiques instructeurs only manage the scientifiques dossiers + register_instructeur_and_log_in(marie.email) + click_on procedure.libelle + expect(page).not_to have_text(litteraire_user.email) + expect(page).to have_text(scientifique_user.email) + log_out + + # TODO: notifications tests + end + + def publish_procedure(procedure) + click_on procedure.libelle + find('#publish-procedure').click + within '#publish-modal' do + fill_in 'lien_site_web', with: 'http://some.website' + click_on 'publish' + end + + expect(page).to have_text('Démarche publiée') + end + + def user_send_dossier(user, groupe) + login_as user, scope: :user + visit commencer_path(path: procedure.reload.path) + click_on 'Commencer la démarche' + + fill_in 'individual_nom', with: 'Nom' + fill_in 'individual_prenom', with: 'Prenom' + click_button('Continuer') + + select(groupe, from: 'dossier_groupe_instructeur_id') + + click_on 'Déposer le dossier' + + log_out + end + + def register_instructeur_and_log_in(email) + confirmation_email = emails_sent_to(email) + .filter { |m| m.subject == 'Activez votre compte instructeur' } + .first + token_params = confirmation_email.body.match(/token=[^"]+/) + + visit "users/activate?#{token_params}" + fill_in :user_password, with: 'démarches-simplifiées-pwd' + + click_button 'Définir le mot de passe' + + expect(page).to have_content 'Mot de passe enregistré' + end + + def log_out + click_on 'Se déconnecter' + end +end diff --git a/spec/helpers/dossier_link_helper_spec.rb b/spec/helpers/dossier_link_helper_spec.rb index 2c7db4af1..7b43b37ec 100644 --- a/spec/helpers/dossier_link_helper_spec.rb +++ b/spec/helpers/dossier_link_helper_spec.rb @@ -15,10 +15,11 @@ describe DossierLinkHelper do end context "when access as instructeur" do - let(:dossier) { create(:dossier) } + let(:procedure) { create(:procedure, :routee) } + let(:dossier) { create(:dossier, groupe_instructeur: procedure.groupe_instructeurs.last) } let(:instructeur) { create(:instructeur) } - before { dossier.procedure.defaut_groupe_instructeur.instructeurs << instructeur } + before { procedure.groupe_instructeurs.last.instructeurs << instructeur } it { expect(helper.dossier_linked_path(instructeur, dossier)).to eq(instructeur_dossier_path(dossier.procedure, dossier)) } end diff --git a/spec/models/groupe_instructeur_spec.rb b/spec/models/groupe_instructeur_spec.rb new file mode 100644 index 000000000..4a3b3ce62 --- /dev/null +++ b/spec/models/groupe_instructeur_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe GroupeInstructeur, type: :model do + let(:procedure) { create(:procedure) } + subject { GroupeInstructeur.new(label: label, procedure: procedure) } + + context 'with no label provided' do + let(:label) { '' } + + it { is_expected.to be_invalid } + end + + context 'with a valid label' do + let(:label) { 'Préfecture de la Marne' } + + it { is_expected.to be_valid } + end + + context 'with a label with extra spaces' do + let(:label) { 'Préfecture de la Marne ' } + before do + subject.save + subject.reload + end + + it { is_expected.to be_valid } + it { expect(subject.label).to eq("Préfecture de la Marne") } + end + + context 'with a label already used for this procedure' do + let(:label) { 'Préfecture de la Marne' } + before do + GroupeInstructeur.create!(label: label, procedure: procedure) + end + + it { is_expected.to be_invalid } + end +end