Merge branch 'dev'

This commit is contained in:
gregoirenovel 2018-08-02 14:33:27 +02:00
commit e56efa49b0
26 changed files with 557 additions and 91 deletions

View file

@ -12,10 +12,12 @@ defaults: &defaults
bundle_restore_cache: &bundle_restore_cache bundle_restore_cache: &bundle_restore_cache
restore_cache: restore_cache:
name: Restore Bundler Package Cache
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }} key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
bundle_save_cache: &bundle_save_cache bundle_save_cache: &bundle_save_cache
save_cache: save_cache:
name: Save Bundler Package Cache
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }} key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
paths: paths:
- ~/vendor/bundle - ~/vendor/bundle
@ -27,13 +29,15 @@ bundle_install: &bundle_install
yarn_restore_cache: &yarn_restore_cache yarn_restore_cache: &yarn_restore_cache
restore_cache: restore_cache:
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }} name: Restore Yarn Package Cache
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
yarn_save_cache: &yarn_save_cache yarn_save_cache: &yarn_save_cache
save_cache: save_cache:
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }} name: Save Yarn Package Cache
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
paths: paths:
- ~/node_modules - ~/.cache/yarn
yarn_install: &yarn_install yarn_install: &yarn_install
run: run:

View file

@ -124,6 +124,7 @@ Une fois `overmind` lancé, et un breakpoint `byebug` inséré dans le code, il
## Régénérer les binstubs ## Régénérer les binstubs
bundle binstub railties --force bundle binstub railties --force
bundle binstub unicorn --force
bin/rake rails:update:bin bin/rake rails:update:bin
## Tâches Super Admin ## Tâches Super Admin

View file

@ -10,11 +10,11 @@
line-height: 20px; line-height: 20px;
background-color: #FFFFFF; background-color: #FFFFFF;
color: $black; color: $black;
cursor: pointer;
text-align: center; text-align: center;
-webkit-appearance: none; -webkit-appearance: none;
&:hover { &:hover:not(:disabled) {
cursor: pointer;
background: $light-grey; background: $light-grey;
text-decoration: none; text-decoration: none;
} }
@ -24,12 +24,17 @@
outline: none; outline: none;
} }
&:disabled {
opacity: 0.5;
filter: saturate(50%);
}
&.primary { &.primary {
color: #FFFFFF; color: #FFFFFF;
border-color: $blue; border-color: $blue;
background-color: $blue; background-color: $blue;
&:hover { &:hover:not(:disabled) {
background: $light-blue; background: $light-blue;
} }
} }
@ -39,7 +44,7 @@
border-color: $blue; border-color: $blue;
background-color: #FFFFFF; background-color: #FFFFFF;
&:hover { &:hover:not(:disabled) {
color: #FFFFFF; color: #FFFFFF;
background: $light-blue; background: $light-blue;
} }
@ -62,7 +67,7 @@
border-color: $green; border-color: $green;
background-color: $green; background-color: $green;
&:hover { &:hover:not(:disabled) {
color: $green; color: $green;
background-color: #FFFFFF; background-color: #FFFFFF;
} }
@ -73,7 +78,7 @@
border-color: $black; border-color: $black;
background-color: $black; background-color: $black;
&:hover { &:hover:not(:disabled) {
color: $black; color: $black;
background-color: #FFFFFF; background-color: #FFFFFF;
} }
@ -84,7 +89,7 @@
border-color: $dark-red; border-color: $dark-red;
background-color: $dark-red; background-color: $dark-red;
&:hover { &:hover:not(:disabled) {
color: $dark-red; color: $dark-red;
background-color: #FFFFFF; background-color: #FFFFFF;
} }

View file

@ -1,13 +1,9 @@
@import "colors"; @import "colors";
@import "constants"; @import "constants";
.dossier-edit { .dossier-header {
.dossier-header {
background-color: $light-grey;
margin-bottom: $default-padding;
.container { .container {
padding: $default-padding; padding-bottom: $default-padding;
} }
h1 { h1 {
@ -17,8 +13,15 @@
vertical-align: -3px; vertical-align: -3px;
} }
} }
}
.dossier-form-actions {
margin-top: $default-padding;
margin-bottom: $default-padding;
text-align: right;
}
}
.dossier-edit {
.prologue { .prologue {
margin: (1.5 * $default-padding) 0; margin: (1.5 * $default-padding) 0;
display: flex; display: flex;

View file

@ -316,6 +316,15 @@
} }
} }
.send-notice {
@include notice-text-style;
margin-bottom: $default-padding;
}
.send-wrapper + .send-notice {
margin-top: - $default-padding;
}
.inline-champ { .inline-champ {
margin-left: $default-spacer; margin-left: $default-spacer;
margin-right: $default-spacer; margin-right: $default-spacer;

View file

@ -0,0 +1,35 @@
@import "constants";
#invites-form {
padding: $default-padding;
text-align: left;
form {
display: flex;
margin-top: $default-padding;
}
h4 {
font-weight: bold;
margin-bottom: $default-spacer;
}
p {
margin-bottom: $default-spacer;
}
ul {
list-style-position: inside;
list-style-type: disc;
margin-bottom: $default-padding;
}
input[type=email] {
width: auto;
margin-bottom: 0;
}
.button {
margin-left: $default-spacer;
}
}

View file

@ -2,10 +2,11 @@ class InvitesController < ApplicationController
before_action :ensure_user_signed_in before_action :ensure_user_signed_in
def create def create
email = params[:email].downcase email = params[:invite_email].downcase
dossier = current_user.dossiers.find(params[:dossier_id])
invite = InviteUser.create( invite = InviteUser.create(
dossier: current_user.dossiers.find(params[:dossier_id]), dossier: dossier,
user: User.find_by(email: email), user: User.find_by(email: email),
email: email, email: email,
email_sender: current_user.email email_sender: current_user.email
@ -18,12 +19,15 @@ class InvitesController < ApplicationController
InviteMailer.invite_guest(invite).deliver_later InviteMailer.invite_guest(invite).deliver_later
end end
flash.notice = "Invitation envoyée (#{invite.email})" flash.notice = "Une invitation a été envoyée à #{invite.email}."
else else
flash.alert = invite.errors.full_messages flash.alert = invite.errors.full_messages
end end
redirect_to url_for(controller: 'users/recapitulatif', action: :show, dossier_id: params['dossier_id']) respond_to do |format|
format.html { redirect_back(fallback_location: helpers.url_for_dossier(dossier)) }
format.js { @dossier = dossier }
end
end end
private private

View file

@ -9,7 +9,11 @@ class Users::Dossiers::InvitesController < UsersController
def show def show
@facade = InviteDossierFacades.new params[:id].to_i, current_user.email @facade = InviteDossierFacades.new params[:id].to_i, current_user.email
if @facade.dossier.brouillon?
redirect_to modifier_dossier_path(@facade.dossier)
else
render 'users/recapitulatif/show' render 'users/recapitulatif/show'
end
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
flash.alert = t('errors.messages.dossier_not_found') flash.alert = t('errors.messages.dossier_not_found')
redirect_to url_for dossiers_path redirect_to url_for dossiers_path

View file

@ -54,6 +54,6 @@ class DossierFacades
end end
def followers def followers
Gestionnaire.joins(:follows).where("follows.dossier_id=#{@dossier.id}") @dossier.followers_gestionnaires
end end
end end

View file

@ -0,0 +1,17 @@
#invites-form
- if dossier.invites.present?
%h4 Personnes invitées à participer à ce dossier
%ul
- dossier.invites.each do |invite|
%li= invite.email
%p Ces personnes peuvent modifier ce dossier.
- if dossier.brouillon?
%p Une fois le dossier complet, vous devez le soumettre vous-même.
- else
%p Vous pouvez inviter quelquun à remplir ce dossier avec vous.
%p Cette personne aura le droit de modifier votre dossier.
= form_tag invites_dossier_path(dossier_id: dossier.id), remote: true, method: :post, class: 'form' do
= email_field_tag :invite_email, '', class: 'small', placeholder: 'adresse email', required: true
= submit_tag 'Envoyer une invitation', class: 'button accepted'

View file

@ -0,0 +1,6 @@
var formView = "<%= escape_javascript(render partial: 'invites/form', locals: { dossier: @dossier }) %>";
document.querySelector("#invites-form").outerHTML = formView;
var flashMessagesView = "<%= escape_javascript(render partial: 'layouts/flash_messages') %>";
document.querySelector("#flash_messages").outerHTML = flashMessagesView;
<% flash.clear %>

View file

@ -1,4 +1,5 @@
- if flash.any? #flash_messages
- if flash.any?
#flash_message.center #flash_message.center
- flash.each do |key, value| - flash.each do |key, value|
- if value.class == Array - if value.class == Array

View file

@ -20,5 +20,5 @@
%li %li
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do = form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email' = text_field_tag :invite_email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invite_email'
= submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" } = submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" }

View file

@ -2,10 +2,8 @@
= render partial: "new_user/dossiers/footer", locals: { dossier: @dossier } = render partial: "new_user/dossiers/footer", locals: { dossier: @dossier }
.dossier-edit .dossier-edit
.dossier-header .dossier-header.sub-header
.container .container
%h1 = render partial: "shared/dossiers/header", locals: { dossier: @dossier, apercu: false }
%span.icon.folder
= @dossier.procedure.libelle
= render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false } = render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false }

View file

@ -58,6 +58,15 @@
= link_to "#", class: "button icon-only" do = link_to "#", class: "button icon-only" do
%span.icon.follow %span.icon.follow
%p
%button.button{ disabled: true } .button.disabled
%button.button.primary{ disabled: true } .button.primary.disabled
%button.button.secondary{ disabled: true } .button.secondary.disabled
%button.button.danger{ disabled: true } .button.danger.disabled
%p %p
= link_to ".button.accepted", "#", class: "button accepted" = link_to ".button.accepted", "#", class: "button accepted"

View file

@ -74,9 +74,10 @@
class: 'button send secondary', class: 'button send secondary',
data: { action: 'draft', disable_with: 'Envoi...' } data: { action: 'draft', disable_with: 'Envoi...' }
- if current_user.owns?(dossier) && dossier.can_transition_to_en_construction? - if dossier.can_transition_to_en_construction?
= f.button 'Soumettre le dossier', = f.button 'Soumettre le dossier',
class: 'button send primary', class: 'button send primary',
disabled: !current_user.owns?(dossier),
data: { action: 'submit', disable_with: 'Envoi...' } data: { action: 'submit', disable_with: 'Envoi...' }
- else - else
@ -84,4 +85,8 @@
class: 'button send primary', class: 'button send primary',
data: { action: 'submit', disable_with: 'Envoi...' } data: { action: 'submit', disable_with: 'Envoi...' }
- if dossier.brouillon? && !current_user.owns?(dossier)
.send-notice.invite-cannot-submit
En tant quinvité, vous pouvez remplir ce formulaire mais le titulaire du dossier doit le soumettre lui-même.
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier } = render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }

View file

@ -0,0 +1,16 @@
%h1
%span.icon.folder
= dossier.procedure.libelle
.dossier-form-actions
- if current_user.owns?(dossier)
%span.button.dropdown.invite-user-action
%span.icon.person
- if dossier.invites.count > 0
Voir les personnes invitées
%span.badge= dossier.invites.count
- else
Inviter une personne à modifier ce dossier
.dropdown-content.fade-in-down
= render partial: "invites/form", locals: { dossier: dossier }

View file

@ -1,3 +1,105 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) # frozen_string_literal: true
load Gem.bin_path('bundler', 'bundle')
#
# This file was generated by Bundler.
#
# The application 'bundle' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "rubygems"
m = Module.new do
module_function
def invoked_as_script?
File.expand_path($0) == File.expand_path(__FILE__)
end
def env_var_version
ENV["BUNDLER_VERSION"]
end
def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
bundler_version = a
end
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
bundler_version = $1 || ">= 0.a"
update_index = i
end
bundler_version
end
def gemfile
gemfile = ENV["BUNDLE_GEMFILE"]
return gemfile if gemfile && !gemfile.empty?
File.expand_path("../../Gemfile", __FILE__)
end
def lockfile
lockfile =
case File.basename(gemfile)
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
end
def lockfile_version
return unless File.file?(lockfile)
lockfile_contents = File.read(lockfile)
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
Regexp.last_match(1)
end
def bundler_version
@bundler_version ||= begin
env_var_version || cli_arg_version ||
lockfile_version || "#{Gem::Requirement.default}.a"
end
end
def load_bundler!
ENV["BUNDLE_GEMFILE"] ||= gemfile
# must dup string for RG < 1.8 compatibility
activate_bundler(bundler_version.dup)
end
def activate_bundler(bundler_version)
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
bundler_version = "< 2"
end
gem_error = activation_error_handling do
gem "bundler", bundler_version
end
return if gem_error.nil?
require_error = activation_error_handling do
require "bundler/version"
end
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
exit 42
end
def activation_error_handling
yield
nil
rescue StandardError, LoadError => e
e
end
end
m.load_bundler!
if m.invoked_as_script?
load Gem.bin_path("bundler", "bundle")
end

29
bin/unicorn Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'unicorn' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path("../bundle", __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("unicorn", "unicorn")

29
bin/unicorn_rails Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'unicorn_rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path("../bundle", __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("unicorn", "unicorn_rails")

View file

@ -11,7 +11,7 @@ describe InvitesController, type: :controller do
sign_in signed_in_profile sign_in signed_in_profile
end end
subject { post :create, params: { dossier_id: dossier.id, email: email } } subject { post :create, params: { dossier_id: dossier.id, invite_email: email } }
context "when gestionnaire is signed_in" do context "when gestionnaire is signed_in" do
let(:signed_in_profile) { create(:gestionnaire) } let(:signed_in_profile) { create(:gestionnaire) }
@ -69,11 +69,16 @@ describe InvitesController, type: :controller do
context 'when user has access to dossier' do context 'when user has access to dossier' do
before do before do
request.env["HTTP_REFERER"] = "/dossiers/#{dossier.id}/modifier"
dossier.update(user: signed_in_profile) dossier.update(user: signed_in_profile)
end end
it { expect { subject }.to change(InviteUser, :count).by(1) } it { expect { subject }.to change(InviteUser, :count).by(1) }
it "redirects to the previous URL" do
expect(subject).to redirect_to("/dossiers/#{dossier.id}/modifier")
end
context 'when email is assign to an user' do context 'when email is assign to an user' do
let! (:user_invite) { create(:user, email: email) } let! (:user_invite) { create(:user, email: email) }

View file

@ -96,6 +96,47 @@ describe NewUser::DossiersController, type: :controller do
end end
end end
describe "#forbid_invite_submission!" do
let(:user) { create(:user) }
let(:asked_dossier) { create(:dossier) }
let(:ensure_authorized) { :forbid_invite_submission! }
let(:submit_action) { 'submit' }
before do
@controller.params = @controller.params.merge(dossier_id: asked_dossier.id, submit_action: submit_action)
allow(@controller).to receive(:current_user).and_return(user)
allow(@controller).to receive(:redirect_to)
end
context 'when a user save their own draft' do
let(:asked_dossier) { create(:dossier, user: user) }
let(:submit_action) { 'draft' }
it_behaves_like 'does not redirect nor flash'
end
context 'when a user submit their own dossier' do
let(:asked_dossier) { create(:dossier, user: user) }
let(:submit_action) { 'submit' }
it_behaves_like 'does not redirect nor flash'
end
context 'when an invite save the draft for a dossier where they where invited' do
before { create(:invite, dossier: asked_dossier, user: user, type: 'InviteUser') }
let(:submit_action) { 'draft' }
it_behaves_like 'does not redirect nor flash'
end
context 'when an invite submit a dossier where they where invited' do
before { create(:invite, dossier: asked_dossier, user: user, type: 'InviteUser') }
let(:submit_action) { 'submit' }
it_behaves_like 'redirects and flashes'
end
end
describe 'attestation' do describe 'attestation' do
before { sign_in(user) } before { sign_in(user) }

View file

@ -1,78 +1,78 @@
describe Users::Dossiers::InvitesController, type: :controller do describe Users::Dossiers::InvitesController, type: :controller do
describe '#authenticate_user!' do describe '#authenticate_user!' do
let(:user) { create :user } let(:user) { create :user }
let(:invite) { create :invite } let(:dossier) { create(:dossier, :en_construction) }
let(:invite) { create(:invite, dossier: dossier) }
subject { get :show, params: { id: invite.id, email: email } }
context 'when email is not set' do context 'when email is not set' do
context 'when user is not connected' do let(:email) { nil }
before do
get :show, params: { id: invite.id }
end
context 'and user is not connected' do
it { is_expected.to redirect_to new_user_session_path } it { is_expected.to redirect_to new_user_session_path }
end end
context 'when user is connected' do context 'and user is connected' do
let!(:invite) { create :invite, user: user } let(:invite) { create :invite, dossier: dossier, user: user }
before { sign_in invite.user }
before do it { is_expected.to have_http_status(:ok) }
sign_in invite.user
get :show, params: { id: invite.id }
end end
it { expect(response.status).to eq 200 }
end
end
context 'when email is set' do
before do
get :show, params: { id: invite.id, email: email }
end end
context 'when email is blank' do context 'when email is blank' do
let(:email) { '' } let(:email) { '' }
it { is_expected.to redirect_to new_user_session_path } it { is_expected.to redirect_to new_user_session_path }
end end
context 'when email is not blank' do context 'when email is not blank' do
context 'when email is affected at an user' do context 'when email is affected at an user' do
let(:email) { user.email } let(:email) { user.email }
it { is_expected.to redirect_to new_user_session_path } it { is_expected.to redirect_to new_user_session_path }
end end
context 'when email is not affected at an user' do context 'when email is not affected at an user' do
let(:email) { 'new_user@octo.com' } let(:email) { 'new_user@octo.com' }
it { is_expected.to redirect_to new_user_registration_path(user_email: email) } it { is_expected.to redirect_to new_user_registration_path(user_email: email) }
end end
end end
end end
end
describe '#GET show' do describe '#GET show' do
let(:user) { create :user } let(:user) { create :user }
let(:dossier) { create :dossier }
let(:invite) { create :invite, email: email, dossier: (create :dossier) } let(:invite) { create :invite, email: email, dossier: dossier }
subject { get :show, params: { id: invite.id } }
before do before do
sign_in user sign_in user
end end
context 'when invitation ID is attach at the user email account' do subject! { get :show, params: { id: invite.id } }
context 'when invitation ID is attached at the user email account' do
let(:email) { user.email } let(:email) { user.email }
it { expect(subject.status).to eq 200 }
context 'and dossier is a brouillon' do
let(:dossier) { create :dossier, state: 'brouillon' }
it { is_expected.to have_http_status(302) }
it { is_expected.to redirect_to modifier_dossier_path(dossier) }
end end
context 'when invitation ID is not attach at the user email account' do context 'and dossier is not a brouillon' do
let(:dossier) { create :dossier, :en_construction }
it { is_expected.to have_http_status(:ok) }
it { is_expected.to render_template('users/recapitulatif/show') }
end
end
context 'when invitation ID is not attached at the user email account' do
let(:email) { 'fake@email.com' } let(:email) { 'fake@email.com' }
it { expect(subject.status).to eq 302 } it { is_expected.to have_http_status(302) }
it { is_expected.to redirect_to dossiers_path } it { is_expected.to redirect_to dossiers_path }
it { expect(flash[:alert]).to be_present }
end end
end end
end end

View file

@ -0,0 +1,135 @@
require 'spec_helper'
feature 'Invitations' do
let(:user) { create(:user) }
let(:invited_user) { create(:user, email: 'user_invite@exemple.fr') }
let(:procedure) { create(:procedure, :published, :with_type_de_champ) }
let(:invite) { create(:invite_user, user: invited_user, dossier: dossier) }
context 'when the dossier is a brouillon' do
let!(:dossier) { create(:dossier, :for_individual, state: 'brouillon', user: user, procedure: procedure) }
scenario 'on the form, a user can invite another user to collaborate on the dossier', js: true do
log_in(user)
navigate_to_brouillon(dossier)
fill_in 'Libelle du champ', with: 'Some edited value'
send_invite_to "user_invite@exemple.fr"
expect(page).to have_current_path(modifier_dossier_path(dossier))
expect(page).to have_text("Une invitation a été envoyée à user_invite@exemple.fr.")
expect(page).to have_text("user_invite@exemple.fr")
# Ensure unsaved edits to the form are not lost
expect(page).to have_field('Libelle du champ', with: 'Some edited value')
end
scenario 'an invited user can see and edit the draft', js: true do
visit users_dossiers_invite_path(invite)
expect(page).to have_current_path(new_user_session_path)
submit_login_form(invited_user)
expect(page).to have_current_path(modifier_dossier_path(dossier))
expect(page).to have_no_selector('.button.invite-user-action')
fill_in 'Libelle du champ', with: 'Some edited value'
click_button 'Enregistrer le brouillon'
expect(page).to have_text('Votre brouillon a bien été sauvegardé')
expect(page).to have_field('Libelle du champ', with: 'Some edited value')
end
scenario 'an invited user cannot submit the draft' do
visit users_dossiers_invite_path(invite)
expect(page).to have_current_path(new_user_session_path)
submit_login_form(invited_user)
expect(page).to have_current_path(modifier_dossier_path(dossier))
expect(page).to have_button('Soumettre le dossier', disabled: true)
expect(page).to have_selector('.invite-cannot-submit')
end
end
context 'when the dossier is en_construction' do
let!(:dossier) { create(:dossier, :for_individual, :en_construction, user: user, procedure: procedure) }
scenario 'on dossier details, a user can invite another user to collaborate on the dossier', js: true do
log_in(user)
navigate_to_recapitulatif(dossier)
legacy_send_invite_to "user_invite@exemple.fr"
expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier))
expect(page).to have_text("Une invitation a été envoyée à user_invite@exemple.fr.")
expect(page).to have_text("user_invite@exemple.fr")
end
scenario 'an invited user can see and edit the dossier', js: true do
visit users_dossiers_invite_path(invite)
expect(page).to have_current_path(new_user_session_path)
submit_login_form(invited_user)
expect(page).to have_current_path(users_dossiers_invite_path(invite))
expect(page).to have_no_selector('.button.invite-user-action')
expect(page).to have_text("Dossier nº #{dossier.id}")
# We should be able to just click() the link, but Capybara detects that the
# enclosing div would be clicked instead.
expect(page).to have_link("MODIFIER", href: modifier_dossier_path(dossier))
visit modifier_dossier_path(dossier)
expect(page).to have_current_path(modifier_dossier_path(dossier))
fill_in "Libelle du champ", with: "Some edited value"
click_button "Enregistrer les modifications du dossier"
expect(page).to have_current_path(users_dossiers_invite_path(invite))
expect(page).to have_text("Some edited value")
end
end
private
def log_in(user)
visit '/'
click_on 'Connexion'
submit_login_form(user)
expect(page).to have_current_path(dossiers_path)
end
def submit_login_form(user)
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_on 'Se connecter'
end
def navigate_to_brouillon(dossier)
expect(page).to have_current_path(dossiers_path)
click_on(dossier.id)
expect(page).to have_current_path(modifier_dossier_path(dossier))
end
def navigate_to_recapitulatif(dossier)
expect(page).to have_current_path(dossiers_path)
click_on(dossier.id)
expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier))
end
def send_invite_to(invited_email)
find('.button.invite-user-action').click()
expect(page).to have_button("Envoyer une invitation", visible: true)
fill_in 'invite_email', with: invited_email
click_on "Envoyer une invitation"
end
def legacy_send_invite_to(invited_email)
find('.dropdown-toggle', text: "Voir les personnes impliquées").click()
expect(page).to have_button("Ajouter", visible: true)
fill_in 'invite_email', with: invited_email
page.accept_alert "Envoyer l'invitation ?" do
click_on "Ajouter"
end
end
end

View file

@ -189,6 +189,14 @@ describe Dossier do
let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") } let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") }
let!(:follow) { create(:follow, gestionnaire: gestionnaire, dossier: dossier) } let!(:follow) { create(:follow, gestionnaire: gestionnaire, dossier: dossier) }
describe "followers_gestionnaires" do
let(:non_following_gestionnaire) { create(:gestionnaire) }
subject { dossier.followers_gestionnaires }
it { expect(subject).to eq [gestionnaire] }
it { expect(subject).not_to include(non_following_gestionnaire) }
end
describe '#export_headers' do describe '#export_headers' do
subject { dossier.export_headers } subject { dossier.export_headers }