commit
bb058acbe6
26 changed files with 192 additions and 272 deletions
|
@ -69,6 +69,11 @@ jobs:
|
||||||
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
|
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
|
||||||
name: Create DB
|
name: Create DB
|
||||||
command: bundle exec rake db:create db:schema:load db:migrate RAILS_ENV=test
|
command: bundle exec rake db:create db:schema:load db:migrate RAILS_ENV=test
|
||||||
|
- run:
|
||||||
|
environment:
|
||||||
|
RAILS_ENV: test
|
||||||
|
name: Precompile Webpack assets
|
||||||
|
command: bin/webpack
|
||||||
- run:
|
- run:
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
|
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
|
|
||||||
%new-type {
|
%new-type {
|
||||||
font-family: "Muli";
|
font-family: "Muli", system-ui, -apple-system, sans-serif;
|
||||||
color: $black;
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ class API::V1::DossiersController < APIController
|
||||||
render json: {}, status: :unauthorized
|
render json: {}, status: :unauthorized
|
||||||
end
|
end
|
||||||
|
|
||||||
@dossiers = @procedure.dossiers.state_not_brouillon
|
@dossiers = @procedure.dossiers.state_not_brouillon.order_for_api
|
||||||
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: {}, status: :not_found
|
render json: {}, status: :not_found
|
||||||
|
|
|
@ -6,4 +6,11 @@ class AttachmentsController < ApplicationController
|
||||||
@attachment = @blob.attachments.find(params[:id])
|
@attachment = @blob.attachments.find(params[:id])
|
||||||
@user_can_upload = params[:user_can_upload]
|
@user_can_upload = params[:user_can_upload]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
attachment = @blob.attachments.find(params[:id])
|
||||||
|
@attachment_id = attachment.id
|
||||||
|
attachment.purge_later
|
||||||
|
flash.now.notice = 'La pièce jointe a bien été supprimée.'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -141,13 +141,6 @@ module Gestionnaires
|
||||||
redirect_to annotations_privees_gestionnaire_dossier_path(procedure, dossier)
|
redirect_to annotations_privees_gestionnaire_dossier_path(procedure, dossier)
|
||||||
end
|
end
|
||||||
|
|
||||||
def purge_champ_piece_justificative
|
|
||||||
@champ = dossier.champs_private.find(params[:champ_id])
|
|
||||||
@champ.piece_justificative_file.purge_later
|
|
||||||
|
|
||||||
flash.notice = 'La pièce jointe a bien été supprimée.'
|
|
||||||
end
|
|
||||||
|
|
||||||
def print
|
def print
|
||||||
@dossier = dossier
|
@dossier = dossier
|
||||||
render layout: "print"
|
render layout: "print"
|
||||||
|
|
|
@ -6,11 +6,11 @@ module Users
|
||||||
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
|
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
|
||||||
|
|
||||||
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new]
|
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new]
|
||||||
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire, :purge_champ_piece_justificative]
|
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire]
|
||||||
|
|
||||||
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||||
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||||
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_brouillon, :modifier, :update, :purge_champ_piece_justificative]
|
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_brouillon, :modifier, :update]
|
||||||
before_action :forbid_invite_submission!, only: [:update_brouillon]
|
before_action :forbid_invite_submission!, only: [:update_brouillon]
|
||||||
before_action :forbid_closed_submission!, only: [:update_brouillon]
|
before_action :forbid_closed_submission!, only: [:update_brouillon]
|
||||||
before_action :show_demarche_en_test_banner
|
before_action :show_demarche_en_test_banner
|
||||||
|
@ -236,14 +236,6 @@ module Users
|
||||||
redirect_to url_for dossiers_path
|
redirect_to url_for dossiers_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def purge_champ_piece_justificative
|
|
||||||
@champ = dossier.champs.find(params[:champ_id])
|
|
||||||
|
|
||||||
@champ.piece_justificative_file.purge_later
|
|
||||||
|
|
||||||
flash.notice = 'La pièce jointe a bien été supprimée.'
|
|
||||||
end
|
|
||||||
|
|
||||||
def dossier_for_help
|
def dossier_for_help
|
||||||
dossier_id = params[:id] || params[:dossier_id]
|
dossier_id = params[:id] || params[:dossier_id]
|
||||||
@dossier || (dossier_id.present? && Dossier.find_by(id: dossier_id.to_i))
|
@dossier || (dossier_id.present? && Dossier.find_by(id: dossier_id.to_i))
|
||||||
|
|
|
@ -13,10 +13,13 @@ export function initMap(element, position, editable = false) {
|
||||||
scrollWheelZoom: false
|
scrollWheelZoom: false
|
||||||
}).setView([position.lat, position.lon], editable ? 18 : position.zoom);
|
}).setView([position.lat, position.lon], editable ? 18 : position.zoom);
|
||||||
|
|
||||||
|
const loadTilesLayer = process.env.RAILS_ENV != 'test';
|
||||||
|
if (loadTilesLayer) {
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
attribution:
|
attribution:
|
||||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
}
|
||||||
|
|
||||||
if (editable) {
|
if (editable) {
|
||||||
const freeDraw = new FreeDraw({
|
const freeDraw = new FreeDraw({
|
||||||
|
|
|
@ -26,14 +26,17 @@ export function delegate(eventNames, selector, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getJSON(url, data, method = 'get') {
|
export function getJSON(url, data, method = 'get') {
|
||||||
|
incrementActiveRequestsCount();
|
||||||
data = method !== 'get' ? JSON.stringify(data) : data;
|
data = method !== 'get' ? JSON.stringify(data) : data;
|
||||||
return $.ajax({
|
return Promise.resolve(
|
||||||
|
$.ajax({
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
});
|
})
|
||||||
|
).finally(decrementActiveRequestsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scrollTo(container, scrollTo) {
|
export function scrollTo(container, scrollTo) {
|
||||||
|
@ -62,3 +65,15 @@ function offset(element) {
|
||||||
left: rect.left + document.body.scrollLeft
|
left: rect.left + document.body.scrollLeft
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DATA_ACTIVE_REQUESTS_COUNT = 'data-active-requests-count';
|
||||||
|
|
||||||
|
function incrementActiveRequestsCount() {
|
||||||
|
const count = document.body.getAttribute(DATA_ACTIVE_REQUESTS_COUNT) || '0';
|
||||||
|
document.body.setAttribute(DATA_ACTIVE_REQUESTS_COUNT, parseInt(count) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrementActiveRequestsCount() {
|
||||||
|
const count = document.body.getAttribute(DATA_ACTIVE_REQUESTS_COUNT) || '0';
|
||||||
|
document.body.setAttribute(DATA_ACTIVE_REQUESTS_COUNT, parseInt(count) - 1);
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ class Dossier < ApplicationRecord
|
||||||
scope :not_archived, -> { where(archived: false) }
|
scope :not_archived, -> { where(archived: false) }
|
||||||
|
|
||||||
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
|
||||||
|
scope :order_for_api, -> (order = :asc) { order(en_construction_at: order, created_at: order, id: order) }
|
||||||
|
|
||||||
scope :all_state, -> { not_archived.state_not_brouillon }
|
scope :all_state, -> { not_archived.state_not_brouillon }
|
||||||
scope :en_construction, -> { not_archived.state_en_construction }
|
scope :en_construction, -> { not_archived.state_en_construction }
|
||||||
|
|
3
app/views/attachments/destroy.js.erb
Normal file
3
app/views/attachments/destroy.js.erb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<%= render_flash(timeout: 5000, sticky: true) %>
|
||||||
|
<%= remove_element("#piece_justificative_#{@attachment_id}") %>
|
||||||
|
<%= show_element("#piece_justificative_file_#{@attachment_id}") %>
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
= form_for @avis, url: gestionnaire_avis_path(@avis), html: { class: 'form' } do |f|
|
= form_for @avis, url: gestionnaire_avis_path(@avis), html: { class: 'form' } do |f|
|
||||||
= f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true
|
= f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true
|
||||||
= render partial: "shared/attachment/update", locals: { pj: @avis.piece_justificative_file, object: @avis, form: f }
|
= render partial: "shared/attachment/update", locals: { attachment: @avis.piece_justificative_file.attachment, user_can_destroy: true, form: f }
|
||||||
|
|
||||||
.flex.justify-between.align-baseline
|
.flex.justify-between.align-baseline
|
||||||
%p.confidentiel.flex
|
%p.confidentiel.flex
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<%= render_flash(timeout: 5000, sticky: true) %>
|
|
||||||
<%= remove_element("#piece_justificative_#{@champ.id}") %>
|
|
||||||
<%= show_element("#champs_#{@champ.id}") %>
|
|
|
@ -1,12 +1,22 @@
|
||||||
.piece-justificative
|
.piece-justificative
|
||||||
- if pj.attached?
|
- if defined?(template) && template.attached?
|
||||||
.piece-justificative-actions{ id: "piece_justificative_#{object.id}" }
|
%p.edit-pj-template.mb-1
|
||||||
|
Veuillez télécharger, remplir et joindre
|
||||||
|
= link_to('le modèle suivant', url_for(template), target: '_blank', rel: 'noopener')
|
||||||
|
|
||||||
|
- attachment_id = attachment ? attachment.id : SecureRandom.uuid
|
||||||
|
- user_can_destroy = defined?(user_can_destroy) ? user_can_destroy : false
|
||||||
|
- if attachment
|
||||||
|
.piece-justificative-actions{ id: "piece_justificative_#{attachment_id}" }
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
= render partial: "shared/attachment/show", locals: { attachment: pj.attachment, user_can_upload: true }
|
= render partial: "shared/attachment/show", locals: { attachment: attachment, user_can_upload: true }
|
||||||
|
- if user_can_destroy
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
= button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#champs_#{object.id}" }
|
= link_to 'Supprimer', attachment_url(attachment.id, { signed_id: attachment.blob.signed_id }), remote: true, method: :delete, class: 'button small danger'
|
||||||
|
.piece-justificative-action
|
||||||
|
= button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#piece_justificative_file_#{attachment_id}" }
|
||||||
|
|
||||||
= form.file_field :piece_justificative_file,
|
= form.file_field :piece_justificative_file,
|
||||||
id: "champs_#{object.id}",
|
id: "piece_justificative_file_#{attachment_id}",
|
||||||
class: "piece-justificative-input #{'hidden' if pj.attached?}",
|
class: "piece-justificative-input #{'hidden' if attachment}",
|
||||||
direct_upload: true
|
direct_upload: true
|
||||||
|
|
|
@ -1,24 +1 @@
|
||||||
- pj = champ.piece_justificative_file
|
= render partial: "shared/attachment/update", locals: { attachment: champ.piece_justificative_file.attachment, template: champ.type_de_champ.piece_justificative_template, user_can_destroy: true, form: form }
|
||||||
|
|
||||||
.piece-justificative
|
|
||||||
- if champ.type_de_champ.piece_justificative_template.attached?
|
|
||||||
%p.edit-pj-template.mb-1
|
|
||||||
Veuillez télécharger, remplir et joindre
|
|
||||||
= link_to('le modèle suivant', url_for(champ.type_de_champ.piece_justificative_template), target: '_blank', rel: 'noopener')
|
|
||||||
|
|
||||||
- if pj.attached?
|
|
||||||
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
|
||||||
.piece-justificative-action
|
|
||||||
= render partial: "shared/attachment/show", locals: { attachment: pj.attachment, user_can_upload: true }
|
|
||||||
.piece-justificative-action
|
|
||||||
- if champ.private?
|
|
||||||
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
|
||||||
- else
|
|
||||||
= link_to 'Supprimer', champ_purge_champ_piece_justificative_path(id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
|
||||||
.piece-justificative-action
|
|
||||||
= button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#champs_#{champ.id}" }
|
|
||||||
|
|
||||||
= form.file_field :piece_justificative_file,
|
|
||||||
id: "champs_#{champ.id}",
|
|
||||||
class: "piece-justificative-input #{'hidden' if pj.attached?}",
|
|
||||||
direct_upload: true
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<%= render_flash(timeout: 5000, sticky: true) %>
|
|
||||||
<%= remove_element("#piece_justificative_#{@champ.id}") %>
|
|
||||||
<%= show_element("#champs_#{@champ.id}") %>
|
|
|
@ -134,6 +134,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
get 'attachments/:id', to: 'attachments#show', as: :attachment
|
get 'attachments/:id', to: 'attachments#show', as: :attachment
|
||||||
|
delete 'attachments/:id', to: 'attachments#destroy'
|
||||||
|
|
||||||
get 'tour-de-france' => 'root#tour_de_france'
|
get 'tour-de-france' => 'root#tour_de_france'
|
||||||
get "patron" => "root#patron"
|
get "patron" => "root#patron"
|
||||||
|
@ -281,10 +282,6 @@ Rails.application.routes.draw do
|
||||||
post 'commentaire' => 'dossiers#create_commentaire'
|
post 'commentaire' => 'dossiers#create_commentaire'
|
||||||
post 'ask_deletion'
|
post 'ask_deletion'
|
||||||
get 'attestation'
|
get 'attestation'
|
||||||
|
|
||||||
resources :champs, only: [] do
|
|
||||||
delete 'purge_champ_piece_justificative' => 'dossiers#purge_champ_piece_justificative'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
collection do
|
collection do
|
||||||
|
@ -330,10 +327,6 @@ Rails.application.routes.draw do
|
||||||
post 'send-to-instructeurs' => 'dossiers#send_to_instructeurs'
|
post 'send-to-instructeurs' => 'dossiers#send_to_instructeurs'
|
||||||
post 'avis' => 'dossiers#create_avis'
|
post 'avis' => 'dossiers#create_avis'
|
||||||
get 'print' => 'dossiers#print'
|
get 'print' => 'dossiers#print'
|
||||||
|
|
||||||
resources :champs, only: [] do
|
|
||||||
delete 'purge_champ_piece_justificative' => 'dossiers#purge_champ_piece_justificative'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
51
spec/controllers/attachments_controller_spec.rb
Normal file
51
spec/controllers/attachments_controller_spec.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe AttachmentsController, type: :controller do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
describe '#destroy' do
|
||||||
|
render_views
|
||||||
|
|
||||||
|
let(:attachment) { champ.piece_justificative_file.attachment }
|
||||||
|
let(:dossier) { create(:dossier, user: user) }
|
||||||
|
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id) }
|
||||||
|
let(:signed_id) { attachment.blob.signed_id }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
delete :destroy, params: { id: attachment.id, signed_id: signed_id }, format: :js
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when authenticated" do
|
||||||
|
before { sign_in(user) }
|
||||||
|
|
||||||
|
context 'and dossier is owned by user' do
|
||||||
|
it { is_expected.to have_http_status(200) }
|
||||||
|
|
||||||
|
it do
|
||||||
|
subject
|
||||||
|
expect(champ.reload.piece_justificative_file.attached?).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and signed_id is invalid' do
|
||||||
|
let(:signed_id) { 'yolo' }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(404) }
|
||||||
|
|
||||||
|
it do
|
||||||
|
subject
|
||||||
|
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not authenticated' do
|
||||||
|
it { is_expected.to have_http_status(401) }
|
||||||
|
|
||||||
|
it do
|
||||||
|
subject
|
||||||
|
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -469,44 +469,4 @@ describe Gestionnaires::DossiersController, type: :controller do
|
||||||
it { expect(champ_repetition.champs.first.value).to eq('text') }
|
it { expect(champ_repetition.champs.first.value).to eq('text') }
|
||||||
it { expect(response).to redirect_to(annotations_privees_gestionnaire_dossier_path(dossier.procedure, dossier)) }
|
it { expect(response).to redirect_to(annotations_privees_gestionnaire_dossier_path(dossier.procedure, dossier)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#purge_champ_piece_justificative' do
|
|
||||||
before { sign_in(gestionnaire) }
|
|
||||||
|
|
||||||
subject { delete :purge_champ_piece_justificative, params: { procedure_id: champ.dossier.procedure.id, dossier_id: champ.dossier.id, champ_id: champ.id }, format: :js }
|
|
||||||
|
|
||||||
context 'when gestionnaire can process dossier' do
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id, private: true) }
|
|
||||||
|
|
||||||
it { is_expected.to have_http_status(200) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'but champ is not linked to this dossier' do
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier: create(:dossier), private: true) }
|
|
||||||
|
|
||||||
it { is_expected.to redirect_to(root_path) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when gestionnaire cannot process dossier' do
|
|
||||||
let(:dossier) { create(:dossier, procedure: create(:procedure)) }
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id, private: true) }
|
|
||||||
|
|
||||||
it { is_expected.to redirect_to(root_path) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -919,47 +919,6 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#purge_champ_piece_justificative' do
|
|
||||||
before { sign_in(user) }
|
|
||||||
|
|
||||||
subject { delete :purge_champ_piece_justificative, params: { id: champ.dossier.id, champ_id: champ.id }, format: :js }
|
|
||||||
|
|
||||||
context 'when dossier is owned by user' do
|
|
||||||
let(:dossier) { create(:dossier, user: user) }
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id) }
|
|
||||||
|
|
||||||
it { is_expected.to have_http_status(200) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'but champ is not linked to this dossier' do
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier: create(:dossier)) }
|
|
||||||
|
|
||||||
it { is_expected.to redirect_to(root_path) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when dossier is not owned by user' do
|
|
||||||
let(:dossier) { create(:dossier, user: create(:user)) }
|
|
||||||
let(:champ) { create(:champ_piece_justificative, dossier_id: dossier.id) }
|
|
||||||
|
|
||||||
it { is_expected.to redirect_to(root_path) }
|
|
||||||
|
|
||||||
it do
|
|
||||||
subject
|
|
||||||
expect(champ.reload.piece_justificative_file.attached?).to be(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#dossier_for_help" do
|
describe "#dossier_for_help" do
|
||||||
before do
|
before do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
feature 'add a new type de piece justificative', js: true do
|
|
||||||
let(:administrateur) { create(:administrateur) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
login_as administrateur, scope: :administrateur
|
|
||||||
end
|
|
||||||
context 'when there is an existing piece justificative' do
|
|
||||||
let(:procedure) { create(:procedure, administrateur: administrateur) }
|
|
||||||
before do
|
|
||||||
# Create a dummy PJ, because adding PJs is no longer allowed on procedures that
|
|
||||||
# do not already have one
|
|
||||||
procedure.types_de_piece_justificative.create(libelle: "dummy PJ")
|
|
||||||
visit admin_procedure_pieces_justificatives_path(procedure)
|
|
||||||
end
|
|
||||||
scenario 'displays a form to add new type de piece justificative' do
|
|
||||||
within '#new_type_de_piece_justificative' do
|
|
||||||
expect(page).to have_css('#procedure_types_de_piece_justificative_attributes_1_libelle')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
context 'when user fills field and submit' do
|
|
||||||
let(:libelle) { 'ma piece' }
|
|
||||||
let(:description) { 'ma description' }
|
|
||||||
before do
|
|
||||||
page.find_by_id('procedure_types_de_piece_justificative_attributes_1_libelle').set(libelle)
|
|
||||||
page.find_by_id('procedure_types_de_piece_justificative_attributes_1_description').set(description)
|
|
||||||
page.click_on 'Ajouter la pièce'
|
|
||||||
wait_for_ajax
|
|
||||||
end
|
|
||||||
subject do
|
|
||||||
procedure.reload
|
|
||||||
procedure.types_de_piece_justificative.second
|
|
||||||
end
|
|
||||||
scenario 'creates new type de piece' do
|
|
||||||
expect(subject.libelle).to eq(libelle)
|
|
||||||
expect(subject.description).to eq(description)
|
|
||||||
end
|
|
||||||
scenario 'displays new created pj' do
|
|
||||||
within '#liste_piece_justificative' do
|
|
||||||
expect(page).to have_css('#procedure_types_de_piece_justificative_attributes_1_libelle')
|
|
||||||
expect(page.body).to match(libelle)
|
|
||||||
expect(page.body).to match(description)
|
|
||||||
end
|
|
||||||
within '#new_type_de_piece_justificative' do
|
|
||||||
expect(page).to have_css('#procedure_types_de_piece_justificative_attributes_2_libelle')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
context 'when user delete pj' do
|
|
||||||
before do
|
|
||||||
pj = procedure.types_de_piece_justificative.second
|
|
||||||
page.find_by_id("delete_type_de_piece_justificative_#{pj.id}").click
|
|
||||||
wait_for_ajax
|
|
||||||
end
|
|
||||||
scenario 'removes pj from page' do
|
|
||||||
within '#liste_piece_justificative' do
|
|
||||||
expect(page).not_to have_css('#procedure_types_de_piece_justificative_attributes_1_libelle')
|
|
||||||
expect(page.body).not_to match(libelle)
|
|
||||||
expect(page.body).not_to match(description)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
context 'when user change existing type de pj' do
|
|
||||||
let(:new_libelle) { 'mon nouveau libelle' }
|
|
||||||
before do
|
|
||||||
page.find_by_id('procedure_types_de_piece_justificative_attributes_1_libelle').set(new_libelle)
|
|
||||||
page.find_by_id('save').click
|
|
||||||
wait_for_ajax
|
|
||||||
end
|
|
||||||
scenario 'saves change in database' do
|
|
||||||
pj = procedure.types_de_piece_justificative.second
|
|
||||||
expect(pj.libelle).to eq(new_libelle)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -121,14 +121,15 @@ feature 'As an administrateur I can edit types de champ', js: true do
|
||||||
|
|
||||||
it "Add carte champ" do
|
it "Add carte champ" do
|
||||||
select('Carte', from: 'champ-0-type_champ')
|
select('Carte', from: 'champ-0-type_champ')
|
||||||
fill_in 'champ-0-libelle', with: 'libellé de champ carte'
|
fill_in 'champ-0-libelle', with: 'Libellé de champ carte', fill_options: { clear: :backspace }
|
||||||
blur
|
|
||||||
check 'Quartiers prioritaires'
|
check 'Quartiers prioritaires'
|
||||||
|
|
||||||
|
wait_until { procedure.types_de_champ.first.quartiers_prioritaires == true }
|
||||||
expect(page).to have_content('Formulaire enregistré')
|
expect(page).to have_content('Formulaire enregistré')
|
||||||
|
|
||||||
preview_window = window_opened_by { click_on 'Prévisualiser le formulaire' }
|
preview_window = window_opened_by { click_on 'Prévisualiser le formulaire' }
|
||||||
within_window(preview_window) do
|
within_window(preview_window) do
|
||||||
expect(page).to have_content('libellé de champ carte')
|
expect(page).to have_content('Libellé de champ carte')
|
||||||
expect(page).to have_content('Quartiers prioritaires')
|
expect(page).to have_content('Quartiers prioritaires')
|
||||||
expect(page).not_to have_content('Cadastres')
|
expect(page).not_to have_content('Cadastres')
|
||||||
end
|
end
|
||||||
|
@ -136,9 +137,10 @@ feature 'As an administrateur I can edit types de champ', js: true do
|
||||||
|
|
||||||
it "Add dropdown champ" do
|
it "Add dropdown champ" do
|
||||||
select('Menu déroulant', from: 'champ-0-type_champ')
|
select('Menu déroulant', from: 'champ-0-type_champ')
|
||||||
fill_in 'champ-0-libelle', with: 'libellé de champ menu déroulant'
|
fill_in 'champ-0-libelle', with: 'Libellé de champ menu déroulant', fill_options: { clear: :backspace }
|
||||||
blur
|
fill_in 'champ-0-drop_down_list_value', with: 'Un menu', fill_options: { clear: :backspace }
|
||||||
fill_in 'champ-0-drop_down_list_value', with: 'Un menu'
|
|
||||||
|
wait_until { procedure.types_de_champ.first.drop_down_list&.value == 'Un menu' }
|
||||||
expect(page).to have_content('Formulaire enregistré')
|
expect(page).to have_content('Formulaire enregistré')
|
||||||
|
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
|
@ -14,7 +14,7 @@ feature 'The user' do
|
||||||
allow(Champs::RegionChamp).to receive(:regions).and_return(['region1', 'region2']).at_least(:once)
|
allow(Champs::RegionChamp).to receive(:regions).and_return(['region1', 'region2']).at_least(:once)
|
||||||
allow(Champs::DepartementChamp).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once)
|
allow(Champs::DepartementChamp).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once)
|
||||||
|
|
||||||
log_in(user.email, password, procedure)
|
log_in(user, procedure)
|
||||||
|
|
||||||
fill_individual
|
fill_individual
|
||||||
|
|
||||||
|
@ -75,11 +75,11 @@ feature 'The user' do
|
||||||
expect(page).to have_field('email', with: 'loulou@yopmail.com')
|
expect(page).to have_field('email', with: 'loulou@yopmail.com')
|
||||||
expect(page).to have_field('phone', with: '1234567890')
|
expect(page).to have_field('phone', with: '1234567890')
|
||||||
expect(page).to have_checked_field('Non')
|
expect(page).to have_checked_field('Non')
|
||||||
expect(page).to have_select('simple_drop_down_list', selected: 'val2')
|
expect(page).to have_selected_value('simple_drop_down_list', selected: 'val2')
|
||||||
expect(page).to have_select('multiple_drop_down_list', selected: ['val1', 'val3'])
|
expect(page).to have_selected_value('multiple_drop_down_list', selected: ['val1', 'val3'])
|
||||||
expect(page).to have_select('pays', selected: 'AUSTRALIE')
|
expect(page).to have_selected_value('pays', selected: 'AUSTRALIE')
|
||||||
expect(page).to have_select('regions', selected: 'region2')
|
expect(page).to have_selected_value('regions', selected: 'region2')
|
||||||
expect(page).to have_select('departement', selected: 'dep2')
|
expect(page).to have_selected_value('departement', selected: 'dep2')
|
||||||
expect(page).to have_checked_field('engagement')
|
expect(page).to have_checked_field('engagement')
|
||||||
expect(page).to have_field('dossier_link', with: '123')
|
expect(page).to have_field('dossier_link', with: '123')
|
||||||
expect(page).to have_text('file.pdf')
|
expect(page).to have_text('file.pdf')
|
||||||
|
@ -93,7 +93,7 @@ feature 'The user' do
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'fill a dossier with repetition', js: true do
|
scenario 'fill a dossier with repetition', js: true do
|
||||||
log_in(user.email, password, procedure_with_repetition)
|
log_in(user, procedure_with_repetition)
|
||||||
|
|
||||||
fill_individual
|
fill_individual
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ feature 'The user' do
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'save an incomplete dossier as draft but cannot not submit it', js: true do
|
scenario 'save an incomplete dossier as draft but cannot not submit it', js: true do
|
||||||
log_in(user.email, password, simple_procedure)
|
log_in(user, simple_procedure)
|
||||||
fill_individual
|
fill_individual
|
||||||
|
|
||||||
# Check an incomplete dossier can be saved as a draft, even when mandatory fields are missing
|
# Check an incomplete dossier can be saved as a draft, even when mandatory fields are missing
|
||||||
|
@ -156,7 +156,7 @@ feature 'The user' do
|
||||||
end
|
end
|
||||||
|
|
||||||
scenario 'adding, replacing and removing attachments', js: true do
|
scenario 'adding, replacing and removing attachments', js: true do
|
||||||
log_in(user.email, password, procedure_with_pj)
|
log_in(user, procedure_with_pj)
|
||||||
fill_individual
|
fill_individual
|
||||||
|
|
||||||
# Add an attachment
|
# Add an attachment
|
||||||
|
@ -194,14 +194,10 @@ feature 'The user' do
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def log_in(email, password, procedure)
|
def log_in(user, procedure)
|
||||||
|
login_as user, scope: :user
|
||||||
|
|
||||||
visit "/commencer/#{procedure.path}"
|
visit "/commencer/#{procedure.path}"
|
||||||
click_on 'J’ai déjà un compte'
|
|
||||||
|
|
||||||
expect(page).to have_current_path(new_user_session_path)
|
|
||||||
sign_in_with(email, password)
|
|
||||||
|
|
||||||
expect(page).to have_current_path("/commencer/#{procedure.path}")
|
|
||||||
click_on 'Commencer la démarche'
|
click_on 'Commencer la démarche'
|
||||||
|
|
||||||
expect(page).to have_content("Données d'identité")
|
expect(page).to have_content("Données d'identité")
|
||||||
|
@ -233,10 +229,10 @@ feature 'The user' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_date_and_time(date, field)
|
def check_date_and_time(date, field)
|
||||||
expect(page).to have_select("#{field}_1i", selected: date.strftime('%Y'))
|
expect(page).to have_selected_value("#{field}_1i", selected: date.strftime('%Y'))
|
||||||
expect(page).to have_select("#{field}_2i", selected: I18n.l(date, format: '%B'))
|
expect(page).to have_selected_value("#{field}_2i", selected: I18n.l(date, format: '%B'))
|
||||||
expect(page).to have_select("#{field}_3i", selected: date.strftime('%-d'))
|
expect(page).to have_selected_value("#{field}_3i", selected: date.strftime('%-d'))
|
||||||
expect(page).to have_select("#{field}_4i", selected: date.strftime('%H'))
|
expect(page).to have_selected_value("#{field}_4i", selected: date.strftime('%H'))
|
||||||
expect(page).to have_select("#{field}_5i", selected: date.strftime('%M'))
|
expect(page).to have_selected_value("#{field}_5i", selected: date.strftime('%M'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,7 +40,7 @@ end
|
||||||
|
|
||||||
Capybara.register_driver :headless_chrome do |app|
|
Capybara.register_driver :headless_chrome do |app|
|
||||||
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
|
||||||
chromeOptions: { args: ['headless', 'disable-gpu', 'disable-dev-shm-usage', 'disable-software-rasterizer', 'mute-audio', 'window-size=1440,900'] }
|
chromeOptions: { args: ['headless', 'disable-dev-shm-usage', 'disable-software-rasterizer', 'mute-audio', 'window-size=1440,900'] }
|
||||||
)
|
)
|
||||||
|
|
||||||
Capybara::Selenium::Driver.new app,
|
Capybara::Selenium::Driver.new app,
|
||||||
|
@ -167,4 +167,22 @@ RSpec.configure do |config|
|
||||||
actual.attributes.with_indifferent_access.except(*ignored) == expected.attributes.with_indifferent_access.except(*ignored)
|
actual.attributes.with_indifferent_access.except(*ignored) == expected.attributes.with_indifferent_access.except(*ignored)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Asserts that a given select element exists in the page,
|
||||||
|
# and that the option(s) with the given value(s) are selected.
|
||||||
|
#
|
||||||
|
# Usage: expect(page).to have_selected_value('Country', selected: 'Australia')
|
||||||
|
#
|
||||||
|
# For large lists, this is much faster than `have_select(location, selected: value)`,
|
||||||
|
# as it doesn’t check that every other options are not selected.
|
||||||
|
RSpec::Matchers.define(:have_selected_value) do |select_locator, options|
|
||||||
|
match do |page|
|
||||||
|
values = options[:selected].is_a?(String) ? [options[:selected]] : options[:selected]
|
||||||
|
|
||||||
|
select_element = page.first(:select, select_locator)
|
||||||
|
select_element && values.all? do |value|
|
||||||
|
select_element.first(:option, value).selected?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,6 +62,18 @@ module FeatureHelpers
|
||||||
def blur
|
def blur
|
||||||
page.find('body').click
|
page.find('body').click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pause
|
||||||
|
$stderr.write 'Spec paused. Press enter to continue:'
|
||||||
|
$stdin.gets
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_until
|
||||||
|
Timeout.timeout(Capybara.default_max_wait_time) do
|
||||||
|
sleep(0.1) until (value = yield)
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
module WaitForAjax
|
module WaitForAjax
|
||||||
def wait_for_ajax
|
def wait_for_ajax
|
||||||
Timeout.timeout(Capybara.default_max_wait_time) do
|
expect(page).to have_selector('body[data-active-requests-count="0"]')
|
||||||
loop until finished_all_ajax_requests?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def finished_all_ajax_requests?
|
|
||||||
page.evaluate_script('jQuery.active').zero?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -3541,9 +3541,9 @@ fsevents@^1.2.7:
|
||||||
node-pre-gyp "^0.12.0"
|
node-pre-gyp "^0.12.0"
|
||||||
|
|
||||||
fstream@^1.0.0, fstream@^1.0.2:
|
fstream@^1.0.0, fstream@^1.0.2:
|
||||||
version "1.0.11"
|
version "1.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
|
resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
|
||||||
integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=
|
integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs "^4.1.2"
|
graceful-fs "^4.1.2"
|
||||||
inherits "~2.0.0"
|
inherits "~2.0.0"
|
||||||
|
@ -3644,7 +3644,7 @@ glob-stream@^6.1.0:
|
||||||
to-absolute-glob "^2.0.0"
|
to-absolute-glob "^2.0.0"
|
||||||
unique-stream "^2.0.2"
|
unique-stream "^2.0.2"
|
||||||
|
|
||||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
|
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
|
||||||
version "7.1.3"
|
version "7.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||||
|
@ -3656,6 +3656,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
path-is-absolute "^1.0.0"
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
glob@^7.1.3:
|
||||||
|
version "7.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
|
||||||
|
integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
global-modules@^1.0.0:
|
global-modules@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
|
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
|
||||||
|
|
Loading…
Reference in a new issue