Merge pull request #3868 from betagouv/dev

2019-05-15-01
This commit is contained in:
Paul Chavard 2019-05-15 12:08:06 +02:00 committed by GitHub
commit f50fd90ffb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 6 additions and 1322 deletions

View file

@ -18,36 +18,3 @@ $(document).on('click', '.cancel', function() {
.find('.confirm') .find('.confirm')
.hide(); .hide();
}); });
$(document).on('change', 'select.form-control.type-champ', function() {
var parent = $(this)
.parent()
.parent();
parent.removeClass('header-section');
parent.children('.drop-down-list').removeClass('show-inline');
parent.children('.pj-template').removeClass('show-inline');
parent.children('.carte-options').removeClass('show-inline');
$('.mandatory', parent).show();
switch (this.value) {
case 'header_section':
parent.addClass('header-section');
break;
case 'drop_down_list':
case 'multiple_drop_down_list':
case 'linked_drop_down_list':
parent.children('.drop-down-list').addClass('show-inline');
break;
case 'piece_justificative':
parent.children('.pj-template').addClass('show-inline');
break;
case 'carte':
parent.children('.carte-options').addClass('show-inline');
break;
case 'explication':
$('.mandatory', parent).hide();
break;
}
});

View file

@ -81,11 +81,7 @@ class Admin::ProceduresController < AdminController
gestionnaire.assign_to_procedure(@procedure) gestionnaire.assign_to_procedure(@procedure)
end end
if Flipflop.new_champs_editor?
redirect_to champs_procedure_path(@procedure) redirect_to champs_procedure_path(@procedure)
else
redirect_to admin_procedure_types_de_champ_path(procedure_id: @procedure.id)
end
end end
end end

View file

@ -1,52 +0,0 @@
class Admin::TypesDeChampController < AdminController
before_action :retrieve_procedure
before_action :procedure_locked?
before_action :reset_procedure, only: [:update, :destroy, :move_up, :move_down]
def destroy
@procedure.types_de_champ.destroy(params[:id])
setup_type_de_champ_service
render 'show', format: :js
rescue ActiveRecord::RecordNotFound
render json: { message: 'Champ not found' }, status: 404
end
def show
setup_type_de_champ_service
end
def update
setup_type_de_champ_service
if @procedure.update(@type_de_champ_service.create_update_procedure_params(params))
flash.now.notice = 'Modifications sauvegardées'
else
flash.now.alert = @procedure.errors.full_messages.join(', ')
end
render 'show', format: :js
end
def move_up
index = params[:index].to_i - 1
if @procedure.switch_types_de_champ(index)
setup_type_de_champ_service
render 'show', format: :js
else
render json: {}, status: 400
end
end
def move_down
if @procedure.switch_types_de_champ(params[:index].to_i)
setup_type_de_champ_service
render 'show', format: :js
else
render json: {}, status: 400
end
end
private
def setup_type_de_champ_service
@type_de_champ_service = TypesDeChampService.new(@procedure)
end
end

View file

@ -1,53 +0,0 @@
class Admin::TypesDeChampPrivateController < AdminController
before_action :retrieve_procedure
before_action :procedure_locked?
before_action :reset_procedure, only: [:update, :destroy, :move_up, :move_down]
def destroy
@procedure.types_de_champ_private.destroy(params[:id])
setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js
rescue ActiveRecord::RecordNotFound
render json: { message: 'Champ not found' }, status: 404
end
def show
setup_type_de_champ_service
render 'admin/types_de_champ/show'
end
def update
setup_type_de_champ_service
if @procedure.update(@type_de_champ_service.create_update_procedure_params(params))
flash.now.notice = 'Modifications sauvegardées'
else
flash.now.alert = @procedure.errors.full_messages.join(', ')
end
render 'admin/types_de_champ/show', format: :js
end
def move_up
index = params[:index].to_i - 1
if @procedure.switch_types_de_champ_private(index)
setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js
else
render json: {}, status: 400
end
end
def move_down
if @procedure.switch_types_de_champ_private(params[:index].to_i)
setup_type_de_champ_service
render 'admin/types_de_champ/show', format: :js
else
render json: {}, status: 400
end
end
private
def setup_type_de_champ_service
@type_de_champ_service = TypesDeChampService.new(@procedure, true)
end
end

View file

@ -14,7 +14,7 @@ addEventListener(START_EVENT, ({ target, detail: { id } }) => {
ProgressBar.start(id); ProgressBar.start(id);
const button = target.form.querySelector('button.primary'); const button = target.form.querySelector('button.primary');
if (button) { if (button) {
delete button.dataset.confirm; button.removeAttribute('data-confirm');
} }
}); });

View file

@ -1,140 +0,0 @@
class TypesDeChampService
include Rails.application.routes.url_helpers
TOGGLES = {
TypeDeChamp.type_champs.fetch(:integer_number) => :champ_integer_number?,
TypeDeChamp.type_champs.fetch(:repetition) => :champ_repetition?
}
def options
types_de_champ = TypeDeChamp.type_de_champs_list_fr
types_de_champ.select! do |tdc|
toggle = TOGGLES[tdc.last]
toggle.blank? || Flipflop.send(toggle)
end
types_de_champ
end
def initialize(procedure, private_type_de_champ = false)
@procedure = procedure
@private_type_de_champ = private_type_de_champ
end
def private?
@private_type_de_champ
end
def active
private? ? 'Annotations privées' : 'Champs'
end
def url
private? ? admin_procedure_types_de_champ_private_path(@procedure) : admin_procedure_types_de_champ_path(@procedure)
end
def types_de_champ
private? ? @procedure.types_de_champ_private : @procedure.types_de_champ
end
def new_type_de_champ
TypeDeChamp.new(private: private?)
end
def fields_for_var
private? ? :types_de_champ_private : :types_de_champ
end
def move_up_url(ff)
private? ? move_up_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_up_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def move_down_url(ff)
private? ? move_down_admin_procedure_types_de_champ_private_path(@procedure, ff.index) : move_down_admin_procedure_types_de_champ_path(@procedure, ff.index)
end
def delete_url(ff)
private? ? admin_procedure_type_de_champ_private_path(@procedure, ff.object.id) : admin_procedure_type_de_champ_path(@procedure, ff.object.id)
end
def add_button_id
private? ? :add_type_de_champ_private : :add_type_de_champ
end
def create_update_procedure_params(params)
attributes = "#{fields_for_var}_attributes"
params_with_ordered_champs = order_champs(params, attributes)
parameters = params_with_ordered_champs
.require(:procedure)
.permit(attributes.to_s => [
:libelle,
:description,
:order_place,
:type_champ,
:id,
:mandatory,
:piece_justificative_template,
:quartiers_prioritaires,
:cadastres,
:parcelles_agricoles,
drop_down_list_attributes: [:value, :id]
])
parameters[attributes].each do |index, param|
param[:private] = private?
if param[:libelle].empty?
parameters[attributes].delete(index.to_s)
end
if param['drop_down_list_attributes'] && param['drop_down_list_attributes']['value']
param['drop_down_list_attributes']['value'] = clean_value(param['drop_down_list_attributes']['value'])
end
end
parameters
end
private
def order_champs(params, attributes)
# It's OK to use an unsafe hash here because the params will then go through
# require / permit methods in #create_update_procedure_params
tdcas = params[:procedure][attributes].to_unsafe_hash.to_a
.map { |_hash_index, tdca| tdca }
tdcas
.select { |tdca| !is_number?(tdca[:custom_order_place]) }
.each { |tdca| tdca[:custom_order_place] = (tdca[:order_place].to_i + 1).to_s }
changed_order_tdcas, ordered_tdcas = tdcas.partition { |tdca| tdca_order_changed?(tdca) }
go_up_tdcas, go_down_tdcas = changed_order_tdcas
.partition { |tdca| tdca[:custom_order_place].to_i < (tdca[:order_place].to_i + 1) }
# needed to make the sort_by work properly
tdcas = go_up_tdcas + ordered_tdcas + go_down_tdcas
ordered_tdcas = tdcas
.sort_by { |tdca| tdca[:custom_order_place].to_i }
.each_with_index { |tdca, index| tdca[:order_place] = index.to_s }
.each_with_index.reduce({}) { |acc, (tdca, hash_index)| acc[hash_index.to_s] = tdca; acc }
params[:procedure][attributes] = ActionController::Parameters.new(ordered_tdcas)
params
end
def is_number?(value)
(value =~ /^[0-9]+$/) == 0
end
def tdca_order_changed?(tdca)
(tdca[:order_place].to_i + 1) != tdca[:custom_order_place].to_i
end
def clean_value(value)
value.split("\r\n").map(&:strip).join("\r\n")
end
end

View file

@ -1,10 +1,9 @@
- champs_editor_path = Flipflop.new_champs_editor? ? champs_procedure_path(@procedure) : admin_procedure_types_de_champ_path(@procedure)
.row.white-back .row.white-back
.alert.alert-info .alert.alert-info
.form-group .form-group
%p %p
Pour vos nouveaux besoins de pièces jointes, nous vous invitons à Pour vos nouveaux besoins de pièces jointes, nous vous invitons à
= link_to(champs_editor_path) do = link_to(champs_procedure_path(@procedure)) do
rajouter des champs rajouter des champs
\ <em>pièce justificative</em> à votre formulaire. \ <em>pièce justificative</em> à votre formulaire.
@ -18,7 +17,7 @@
%li Support des pièces de grande taille (jusquà 200 Mo par pièce) %li Support des pièces de grande taille (jusquà 200 Mo par pièce)
%li Pas de limite de soumission simultanée de plusieurs pièces, pour une expérience usager plus confortable %li Pas de limite de soumission simultanée de plusieurs pièces, pour une expérience usager plus confortable
= link_to(champs_editor_path, class: 'btn btn-success') do = link_to(champs_procedure_path(@procedure), class: 'btn btn-success') do
Ajouter un champ PJ Ajouter un champ PJ
- if @procedure.has_old_pjs? - if @procedure.has_old_pjs?

View file

@ -1,86 +0,0 @@
= f.fields_for type_de_champ_service.fields_for_var, types_de_champ, remote: true do |ff|
- type_champ = ff.object.type_champ
.form-inline{ class: (type_champ == TypeDeChamp.type_champs.fetch(:header_section) ? 'header-section' : nil) }
.form-group.libelle
%h4 Libellé
= ff.text_field :libelle, class: 'form-control libelle', placeholder: 'Libellé'
.form-group.type
%h4 Type
= ff.select :type_champ, type_de_champ_service.options, {}, { class: 'form-control type-champ' }
.form-group.description
%h4 Description
= ff.text_area :description, class: 'form-control description', placeholder: 'Description', rows: 3
.form-group.drop-down-list{ class: (%w(drop_down_list multiple_drop_down_list linked_drop_down_list).include?(type_champ) ? 'show-inline' : nil), style: 'margin-right: 5px;' }
%h4 Liste déroulante
= ff.fields_for :drop_down_list_attributes, ff.object.drop_down_list do |fff|
~ fff.text_area :value, class: 'form-control drop_down_list', placeholder: "Ecrire une valeur par ligne et --valeur-- pour un séparateur.", rows: 3, cols: 30
= fff.hidden_field :id
.form-group.pj-template{ class: (type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative)) ? 'show-inline' : nil }
%h4 Modèle
- if type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative)
- template = ff.object.piece_justificative_template
- if !template.attached?
= ff.file_field :piece_justificative_template,
direct_upload: true
- else
= link_to template.filename.to_s, url_for(template), target: '_blank', rel: 'noopener'
%br
Modifier :
= ff.file_field :piece_justificative_template,
direct_upload: true
- else
= ff.file_field :piece_justificative_template,
direct_upload: true
.form-group.carte-options{ class: (type_champ == TypeDeChamp.type_champs.fetch(:carte)) ? 'show-inline' : nil }
%h4 Utilisation de la cartographie
%label
= ff.check_box :quartiers_prioritaires
Quartiers prioritaires
%br
%label
= ff.check_box :cadastres
Cadastre
%br
%label
= ff.check_box :parcelles_agricoles
Parcelles Agricoles
- hide_mandatory = (ff.object.private? || type_champ == TypeDeChamp.type_champs.fetch(:explication))
.form-group.mandatory{ style: hide_mandatory ? 'visibility: hidden;' : nil }
%h4 Obligatoire ?
.center
= ff.check_box :mandatory, placeholder: 'Obligatoire ?'
.form-group
= ff.hidden_field :order_place, value: ff.index
= ff.hidden_field :id
- if ff.object.id.present?
.form-group
%br &nbsp;
- kind = type_de_champ_service.private? ? "private" : "public"
= button_up(@procedure, kind, ff.index, type_de_champ_service.move_up_url(ff))
= button_down(@procedure, kind, ff.index, type_de_champ_service.move_down_url(ff))
.form-group
%h4 position
= ff.number_field :custom_order_place, value: (ff.index + 1), length: 999, class: 'form-control', style: 'width: 100px;'
.form-group
%br &nbsp;
- if ff.object.id.nil?
= f.button 'Ajouter le champ',
id: type_de_champ_service.add_button_id,
class: 'btn btn-success',
data: { disable: true }
- else
= link_to("", type_de_champ_service.delete_url(ff), method: :delete, remote: true, id: "delete_type_de_champ_#{ff.object.id}", class: %w(form-control btn btn-danger fa fa-trash-o) )
%div{ style: 'background-color: rgb(204, 204, 204); height: 1px; margin: 30px auto;' }

View file

@ -1,11 +0,0 @@
= form_for [:admin, procedure], url: type_de_champ_service.url, remote: true do |f|
= render partial: 'admin/types_de_champ/fields', locals: { type_de_champ_service: type_de_champ_service, types_de_champ: type_de_champ_service.types_de_champ, f: f }
= f.button 'Enregistrer',
id: :save,
class: 'btn btn-success',
data: { disable: true }
%hr
#new_type_de_champ
= render partial: 'admin/types_de_champ/fields', locals: { type_de_champ_service: type_de_champ_service, types_de_champ: type_de_champ_service.new_type_de_champ, f: f }

View file

@ -1,3 +0,0 @@
.row.white-back
#liste-champ
= render partial: 'admin/types_de_champ/form', locals: { procedure: @procedure, type_de_champ_service: @type_de_champ_service }

View file

@ -1,2 +0,0 @@
<%= render_flash(timeout: 3000, sticky: true) %>
<%= render_to_element('#liste-champ', partial: 'admin/types_de_champ/form', locals: { procedure: @procedure, type_de_champ_service: @type_de_champ_service }) %>

View file

@ -1 +0,0 @@
= render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: 'Prévisualisation' }

View file

@ -38,8 +38,7 @@
%p.missing-steps (à compléter) %p.missing-steps (à compléter)
- if !@procedure.locked? - if !@procedure.locked?
- champs_editor_path = Flipflop.new_champs_editor? ? champs_procedure_path(@procedure) : admin_procedure_types_de_champ_path(@procedure) %a#onglet-champs{ href: champs_procedure_path(@procedure) }
%a#onglet-champs{ href: champs_editor_path }
.procedure-list-element{ class: ('active' if active == 'Champs') } .procedure-list-element{ class: ('active' if active == 'Champs') }
Champs Champs
@ -49,8 +48,7 @@
Pièces jointes Pièces jointes
- if !@procedure.locked? - if !@procedure.locked?
- annotations_editor_path = Flipflop.new_champs_editor? ? annotations_procedure_path(@procedure) : admin_procedure_types_de_champ_private_path(@procedure) %a#onglet-private-champs{ href: annotations_procedure_path(@procedure) }
%a#onglet-private-champs{ href: annotations_editor_path }
.procedure-list-element{ class: ('active' if active == 'Annotations privées') } .procedure-list-element{ class: ('active' if active == 'Annotations privées') }
Annotations privées Annotations privées

View file

@ -1 +0,0 @@
= render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: @type_de_champ_service.active }

View file

@ -1 +0,0 @@
= render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: "Annotations privées" }

View file

@ -1,46 +0,0 @@
#first-block
.en-cours
= dossier_count = current_user.dossiers.count
= "Dossier".pluralize(dossier_count)
%br
EN COURS
#action-block
#menu-block
.split-hr-left
#procedure-list
- if current_user.dossiers.count != 0
%a{ :href => "#{url_for users_dossiers_path(liste: 'brouillon')}", 'data-toggle' => :tooltip, title: 'Les dossiers jamais proposés à la relecture.' }
.procedure-list-element#brouillon{ class: ('active' if @liste == 'brouillon') }
Brouillons
.badge.progress-bar-default
= @user_dossiers.state_brouillon.count
%a{ :href => "#{url_for users_dossiers_path(liste: 'a_traiter')}", 'data-toggle' => :tooltip, title: 'Les dossiers qui requièrent une action de votre part.' }
.procedure-list-element#a_traiter{ class: ('active' if @liste == 'a_traiter') }
En construction
.badge.progress-bar-danger
= @user_dossiers.state_en_construction.count
%a{ :href => "#{url_for users_dossiers_path(liste: 'en_instruction')}", 'data-toggle' => :tooltip, title: 'Les dossiers en cours d\'examen par l\'administration compétante.' }
.procedure-list-element#en_instruction{ class: ('active' if @liste == 'en_instruction') }
En instruction
.badge.progress-bar-default
= @user_dossiers.state_en_instruction.count
%a{ :href => "#{url_for users_dossiers_path(liste: 'termine')}", 'data-toggle' => :tooltip, title: 'Les dossiers cloturés qui peuvent être "Accepté", "Refusé" ou "Sans suite".' }
.procedure-list-element#termine{ class: ('active' if @liste == 'termine') }
Terminé
.badge.progress-bar-success
= @user_dossiers.state_termine.count
%a{ :href => "#{url_for users_dossiers_path(liste: 'invite')}" }
.procedure-list-element#invite{ class: ('active' if @liste == 'invite') }
Invitation
.badge.progress-bar-warning
= current_user.invites.count
.split-hr-left
#infos-block

View file

@ -15,7 +15,6 @@ Flipflop.configure do
feature :web_hook feature :web_hook
feature :enable_email_login_token feature :enable_email_login_token
feature :new_champs_editor
feature :operation_log_serialize_subject feature :operation_log_serialize_subject

View file

@ -189,18 +189,6 @@ Rails.application.routes.draw do
delete :delete_notice delete :delete_notice
end end
resources :types_de_champ, only: [:destroy]
resource :types_de_champ, only: [:show, :update] do
post '/:index/move_up' => 'types_de_champ#move_up', as: :move_up
post '/:index/move_down' => 'types_de_champ#move_down', as: :move_down
end
resources :types_de_champ_private, only: [:destroy]
resource :types_de_champ_private, only: [:show, :update] do
post '/:index/move_up' => 'types_de_champ_private#move_up', as: :move_up
post '/:index/move_down' => 'types_de_champ_private#move_down', as: :move_down
end
resource :pieces_justificatives, only: [:show, :update] resource :pieces_justificatives, only: [:show, :update]
resources :pieces_justificatives, only: :destroy resources :pieces_justificatives, only: :destroy
resource :pieces_justificatives, only: [:show, :update] do resource :pieces_justificatives, only: [:show, :update] do

View file

@ -186,7 +186,6 @@ describe Admin::ProceduresController, type: :controller do
context 'when procedure is correctly save' do context 'when procedure is correctly save' do
before do before do
Flipflop::FeatureSet.current.test!.switch!(:new_champs_editor, true)
post :create, params: { procedure: procedure_params } post :create, params: { procedure: procedure_params }
end end

View file

@ -1,225 +0,0 @@
require 'spec_helper'
describe Admin::TypesDeChampController, type: :controller do
let(:admin) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateur: admin) }
before do
sign_in admin
end
describe 'GET #show' do
let(:procedure) { create(:procedure, :published, administrateur: admin) }
let(:procedure_id) { procedure.id }
subject { get :show, params: { procedure_id: procedure_id } }
context 'when procedure is not found' do
let(:procedure_id) { 9_999_999 }
it { expect(subject.status).to eq(404) }
end
context 'when procedure is published' do
it { is_expected.to redirect_to admin_procedure_path id: procedure_id }
end
context 'when procedure does not belong to admin' do
let(:admin_2) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateur: admin_2) }
it { expect(subject.status).to eq(404) }
end
end
describe '#update' do
let(:libelle) { 'mon libelle' }
let(:type_champ) { TypeDeChamp.type_champs.fetch(:header_section) }
let(:description) { 'titi' }
let(:order_place) { '' }
let(:types_de_champ_id) { '' }
let(:mandatory) { 'on' }
let(:procedure_params) do
{
types_de_champ_attributes: {
'0' => {
libelle: libelle,
type_champ: type_champ,
description: description,
order_place: order_place,
id: types_de_champ_id,
mandatory: mandatory
},
'1' => {
libelle: '',
type_champ: 'text',
description: '',
order_place: '1',
id: '',
mandatory: false
}
}
}
end
let(:request) { put :update, params: { procedure_id: procedure.id, procedure: procedure_params }, format: :js }
context 'when procedure is found' do
it { expect { request }.to change(TypeDeChamp, :count).by(1) }
describe 'created type de champ' do
before do
request
procedure.reload
end
subject { procedure.types_de_champ.first }
it { expect(subject.libelle).to eq('mon libelle') }
it { expect(subject.type_champ).to eq(TypeDeChamp.type_champs.fetch(:header_section)) }
it { expect(subject.description).to eq('titi') }
end
context 'when type_champ is header_section and mandatory is true' do
let(:type_champ) { TypeDeChamp.type_champs.fetch(:header_section) }
let(:mandatory) { 'on' }
before do
request
procedure.reload
end
subject { procedure.types_de_champ.first }
it { expect(subject.type_champ).to eq type_champ }
it { expect(subject.mandatory).to be_falsey }
end
context 'when type_de_champ already exist' do
let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
let(:type_de_champ) { procedure.types_de_champ.first }
let(:types_de_champ_id) { type_de_champ.id }
let(:libelle) { 'toto' }
let(:type_champ) { TypeDeChamp.type_champs.fetch(:header_section) }
let(:description) { 'citrouille' }
let(:order_place) { '0' }
let(:mandatory) { 'on' }
before do
request
procedure.reload
end
subject { procedure.types_de_champ.first }
it { expect(subject.libelle).to eq('toto') }
it { expect(subject.type_champ).to eq(TypeDeChamp.type_champs.fetch(:header_section)) }
it { expect(subject.description).to eq('citrouille') }
it { expect(subject.order_place).to eq(0) }
it { expect(subject.order_place).to be_truthy }
it { expect(subject.mandatory).to be_falsey }
end
end
context 'when procedure is not found' do
subject { put :update, format: :js, params: { procedure_id: 9_999_999, procedure: procedure_params } }
it 'creates type de champ' do
expect(subject.status).to eq(404)
end
end
end
describe '#destroy' do
before do
delete :destroy, params: { procedure_id: procedure.id, id: type_de_champ_id, format: :js }
end
context 'when type de champs does not exist' do
let(:type_de_champ_id) { 99999999 }
it { expect(subject.status).to eq(404) }
end
context 'when types_de_champ exists' do
let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
let(:type_de_champ_id) { procedure.types_de_champ.first.id }
it { expect(subject.status).to eq(200) }
it 'destroy type de champ' do
procedure.reload
expect(procedure.types_de_champ.count).to eq(0)
end
end
context 'when procedure and type de champs are not linked' do
let(:type_de_champ) { create(:type_de_champ) }
let(:type_de_champ_id) { type_de_champ.id }
it { expect(subject.status).to eq(404) }
end
end
describe 'POST #move_up' do
subject { post :move_up, params: { procedure_id: procedure.id, index: index, format: :js } }
context 'when procedure have no type de champ' do
let(:index) { 0 }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have only one type de champ' do
let(:index) { 1 }
let!(:type_de_champ) { create(:type_de_champ, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have tow type de champs' do
context 'when index == 0' do
let(:index) { 0 }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure) }
let!(:type_de_champ_2) { create(:type_de_champ, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when index > 0' do
let(:index) { 1 }
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
it { expect(subject.status).to eq(200) }
it { expect(subject).to render_template('show') }
it 'changes order places' do
post :move_up, params: { procedure_id: procedure.id, index: index, format: :js }
type_de_champ_0.reload
type_de_champ_1.reload
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end
end
describe 'POST #move_down' do
let(:request) { post :move_down, params: { procedure_id: procedure.id, index: index, format: :js } }
let(:index) { 0 }
subject { request }
context 'when procedure have no type de champ' do
it { expect(subject.status).to eq(400) }
end
context 'when procedure have only one type de champ' do
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have 2 type de champ' do
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
context 'when index represent last type_de_champ' do
let(:index) { 1 }
it { expect(subject.status).to eq(400) }
end
context 'when index does not represent last type_de_champ' do
let(:index) { 0 }
it { expect(subject.status).to eq(200) }
it { expect(subject).to render_template('show') }
it 'changes order place' do
request
type_de_champ_0.reload
type_de_champ_1.reload
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end
end
end

View file

@ -1,207 +0,0 @@
require 'spec_helper'
describe Admin::TypesDeChampPrivateController, type: :controller do
let(:admin) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateur: admin) }
before do
sign_in admin
end
describe 'GET #show' do
let(:procedure) { create(:procedure, :published, administrateur: admin) }
let(:procedure_id) { procedure.id }
subject { get :show, params: { procedure_id: procedure_id } }
context 'when procedure is not found' do
let(:procedure_id) { 9_999_999 }
it { expect(subject.status).to eq(404) }
end
context 'when procedure is published' do
it { is_expected.to redirect_to admin_procedure_path id: procedure_id }
end
context 'when procedure does not belong to admin' do
let(:admin_2) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateur: admin_2) }
it { expect(subject.status).to eq(404) }
end
end
describe '#update' do
let(:libelle) { 'mon libelle' }
let(:type_champ) { TypeDeChamp.type_champs.fetch(:text) }
let(:description) { 'titi' }
let(:order_place) { '' }
let(:types_de_champ_id) { '' }
let(:mandatory) { 'on' }
let(:procedure_params) do
{
types_de_champ_private_attributes: {
'0' => {
libelle: libelle,
type_champ: type_champ,
description: description,
order_place: order_place,
id: types_de_champ_id,
mandatory: mandatory
},
'1' => {
libelle: '',
type_champ: 'text',
description: '',
order_place: '1',
id: '',
mandatory: false
}
}
}
end
let(:request) { put :update, params: { format: :js, procedure_id: procedure.id, procedure: procedure_params } }
context 'when procedure is found' do
it { expect { request }.to change(TypeDeChamp, :count).by(1) }
describe 'created type de champ' do
before do
request
procedure.reload
end
subject { procedure.types_de_champ_private.first }
it { expect(subject.libelle).to eq('mon libelle') }
it { expect(subject.type_champ).to eq('text') }
it { expect(subject.description).to eq('titi') }
it { expect(subject.mandatory).to be_truthy }
end
context 'when type_de_champ already exist' do
let(:procedure) { create(:procedure, :with_type_de_champ_private, administrateur: admin) }
let(:type_de_champ) { procedure.types_de_champ_private.first }
let(:types_de_champ_id) { type_de_champ.id }
let(:libelle) { 'toto' }
let(:type_champ) { TypeDeChamp.type_champs.fetch(:text) }
let(:description) { 'citrouille' }
let(:order_place) { '0' }
let(:mandatory) { 'on' }
before do
request
procedure.reload
end
subject { procedure.types_de_champ_private.first }
it { expect(subject.libelle).to eq('toto') }
it { expect(subject.type_champ).to eq(TypeDeChamp.type_champs.fetch(:text)) }
it { expect(subject.description).to eq('citrouille') }
it { expect(subject.order_place).to eq(0) }
it { expect(subject.order_place).to be_truthy }
end
end
context 'when procedure is not found' do
subject { put :update, params: { format: :js, procedure_id: 9_999_999, procedure: procedure_params } }
it 'creates type de champ' do
expect(subject.status).to eq(404)
end
end
end
describe '#destroy' do
before do
delete :destroy, params: { procedure_id: procedure.id, id: type_de_champ_id, format: :js }
end
context 'when type de champs does not exist' do
let(:type_de_champ_id) { 99999999 }
it { expect(subject.status).to eq(404) }
end
context 'when types_de_champ exists' do
let(:procedure) { create(:procedure, :with_type_de_champ_private, administrateur: admin) }
let(:type_de_champ_id) { procedure.types_de_champ_private.first.id }
it { expect(subject.status).to eq(200) }
it 'destroy type de champ' do
procedure.reload
expect(procedure.types_de_champ.count).to eq(0)
end
end
context 'when procedure and type de champs are not linked' do
let(:type_de_champ) { create(:type_de_champ) }
let(:type_de_champ_id) { type_de_champ.id }
it { expect(subject.status).to eq(404) }
end
end
describe 'POST #move_up' do
subject { post :move_up, params: { procedure_id: procedure.id, index: index, format: :js } }
context 'when procedure have no type de champ' do
let(:index) { 0 }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have only one type de champ' do
let(:index) { 1 }
let!(:type_de_champ) { create(:type_de_champ, :private, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have tow type de champs' do
context 'when index == 0' do
let(:index) { 0 }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure) }
let!(:type_de_champ_2) { create(:type_de_champ, :private, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when index > 0' do
let(:index) { 1 }
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
it { expect(subject.status).to eq(200) }
it { expect(subject).to render_template('show') }
it 'changes order places' do
post :move_up, params: { procedure_id: procedure.id, index: index, format: :js }
type_de_champ_0.reload
type_de_champ_1.reload
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end
end
describe 'POST #move_down' do
let(:request) { post :move_down, params: { procedure_id: procedure.id, index: index, format: :js } }
let(:index) { 0 }
subject { request }
context 'when procedure have no type de champ' do
it { expect(subject.status).to eq(400) }
end
context 'when procedure have only one type de champ' do
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure) }
it { expect(subject.status).to eq(400) }
end
context 'when procedure have 2 type de champ' do
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
context 'when index represent last type_de_champ' do
let(:index) { 1 }
it { expect(subject.status).to eq(400) }
end
context 'when index does not represent last type_de_champ' do
let(:index) { 0 }
it { expect(subject.status).to eq(200) }
it { expect(subject).to render_template('show') }
it 'changes order place' do
request
type_de_champ_0.reload
type_de_champ_1.reload
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end
end
end

View file

@ -1,89 +0,0 @@
require 'spec_helper'
feature 'add a new type de champs', js: true do
let(:administrateur) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateur: administrateur) }
before do
login_as administrateur, scope: :administrateur
visit admin_procedure_types_de_champ_path(procedure)
end
scenario 'displays a form for type de champs' do
expect(page).to have_css('#procedure_types_de_champ_attributes_0_libelle')
expect(page).to have_css('#procedure_types_de_champ_attributes_0_type_champ')
expect(page).to have_css('#procedure_types_de_champ_attributes_0_description')
expect(page).to have_css('#add_type_de_champ')
end
context 'user fill a new type de champ', js: true do
let(:libelle) { 'mon libelle' }
let(:type_champ) { TypeDeChamp.type_champs.fetch(:text) }
let(:description) { 'ma super histoire' }
before do
page.find_by_id('procedure_types_de_champ_attributes_0_libelle').set libelle
page.find_by_id('procedure_types_de_champ_attributes_0_type_champ').set type_champ
page.find_by_id('procedure_types_de_champ_attributes_0_description').set description
click_button 'Ajouter le champ'
wait_for_ajax
procedure.reload
end
subject { procedure.types_de_champ.first }
scenario 'creates the type de champ', js: true do
expect(page).to have_css('#procedure_types_de_champ_attributes_1_libelle')
expect(subject.libelle).to eq(libelle)
expect(subject.type_champ).to eq(type_champ)
expect(subject.description).to eq(description)
expect(subject.order_place).to eq(0)
end
context 'user fill another one' do
let(:libelle) { 'coucou' }
let(:type_champ_value) { TypeDeChamp.type_champs.fetch(:textarea) }
let(:type_champ_label) { 'Zone de texte' }
let(:description) { 'to be or not to be' }
before do
page.find_by_id('procedure_types_de_champ_attributes_1_libelle').set libelle
select(type_champ_label, from: 'procedure_types_de_champ_attributes_1_type_champ')
page.find_by_id('procedure_types_de_champ_attributes_1_description').set description
click_button 'Ajouter le champ'
wait_for_ajax
procedure.reload
end
subject { procedure.types_de_champ.last }
scenario 'creates another types_de_champ' do
expect(page).to have_css('#procedure_types_de_champ_attributes_2_libelle')
expect(subject.libelle).to eq(libelle)
expect(subject.type_champ).to eq(type_champ_value)
expect(subject.description).to eq(description)
expect(subject.order_place).to eq(1)
expect(procedure.types_de_champ.count).to eq(2)
end
context 'user delete the first one' do
let(:type_de_champ) { procedure.types_de_champ.first }
before do
page.find_by_id("delete_type_de_champ_#{type_de_champ.id}").click
wait_for_ajax
procedure.reload
end
scenario 'deletes type de champ' do
expect(procedure.types_de_champ.count).to eq(1)
end
end
context 'user modifies the first one' do
let(:new_libelle) { 'my new field' }
before do
page.find_by_id('procedure_types_de_champ_attributes_0_libelle').set(new_libelle)
page.find_by_id('save').click
wait_for_ajax
procedure.reload
end
scenario 'saves changes in database' do
type_de_champ = procedure.types_de_champ.first
expect(type_de_champ.libelle).to eq(new_libelle)
end
end
end
end
end

View file

@ -1,29 +0,0 @@
require 'spec_helper'
feature 'move down button type de champs', js: true do
let(:administrateur) { create(:administrateur) }
before do
login_as administrateur, scope: :administrateur
end
let(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
let!(:type_de_champ_2) { create(:type_de_champ, :private, procedure: procedure, order_place: 2) }
let!(:type_de_champ_3) { create(:type_de_champ, :private, procedure: procedure, order_place: 3) }
context 'when clicking on move down for type de champ 1' do
before do
visit admin_procedure_types_de_champ_private_path procedure.id
page.find_by_id('btn_down_1').click
wait_for_ajax
type_de_champ_1.reload
type_de_champ_2.reload
end
scenario 'it switches type_de_champ 1 and 2 place ' do
expect(type_de_champ_1.order_place).to eq(2)
expect(type_de_champ_2.order_place).to eq(1)
end
end
end

View file

@ -1,29 +0,0 @@
require 'spec_helper'
feature 'move down button type de champs', js: true do
let(:administrateur) { create(:administrateur) }
before do
login_as administrateur, scope: :administrateur
end
let(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
let!(:type_de_champ_2) { create(:type_de_champ, procedure: procedure, order_place: 2) }
let!(:type_de_champ_3) { create(:type_de_champ, procedure: procedure, order_place: 3) }
context 'when clicking on move down for type de champ 1' do
before do
visit admin_procedure_types_de_champ_path procedure.id
page.find_by_id('btn_down_1').click
wait_for_ajax
type_de_champ_1.reload
type_de_champ_2.reload
end
scenario 'it switches type_de_champ 1 and 2 place ' do
expect(type_de_champ_1.order_place).to eq(2)
expect(type_de_champ_2.order_place).to eq(1)
end
end
end

View file

@ -1,29 +0,0 @@
require 'spec_helper'
feature 'move up button type de champs', js: true do
let(:administrateur) { create(:administrateur) }
before do
login_as administrateur, scope: :administrateur
end
let(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
let!(:type_de_champ_2) { create(:type_de_champ, :private, procedure: procedure, order_place: 2) }
let!(:type_de_champ_3) { create(:type_de_champ, :private, procedure: procedure, order_place: 3) }
context 'when clicking on move down for type de champ 1' do
before do
visit admin_procedure_types_de_champ_private_path procedure.id
page.find_by_id('btn_up_1').click
wait_for_ajax
type_de_champ_0.reload
type_de_champ_1.reload
end
scenario 'it switches type_de_champ 1 and 2 place ' do
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end

View file

@ -1,29 +0,0 @@
require 'spec_helper'
feature 'move up button type de champs', js: true do
let(:administrateur) { create(:administrateur) }
before do
login_as administrateur, scope: :administrateur
end
let(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
let!(:type_de_champ_2) { create(:type_de_champ, procedure: procedure, order_place: 2) }
let!(:type_de_champ_3) { create(:type_de_champ, procedure: procedure, order_place: 3) }
context 'when clicking on move down for type de champ 1' do
before do
visit admin_procedure_types_de_champ_path procedure.id
page.find_by_id('btn_up_1').click
wait_for_ajax
type_de_champ_0.reload
type_de_champ_1.reload
end
scenario 'it switches type_de_champ 1 and 2 place ' do
expect(type_de_champ_0.order_place).to eq(1)
expect(type_de_champ_1.order_place).to eq(0)
end
end
end

View file

@ -5,10 +5,8 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
include ProcedureSpecHelper include ProcedureSpecHelper
let(:administrateur) { create(:administrateur, :with_procedure) } let(:administrateur) { create(:administrateur, :with_procedure) }
let(:test_strategy) { Flipflop::FeatureSet.current.test! }
before do before do
test_strategy.switch!(:new_champs_editor, true)
login_as administrateur, scope: :administrateur login_as administrateur, scope: :administrateur
visit root_path visit root_path
end end

View file

@ -1,112 +0,0 @@
require 'spec_helper'
describe TypesDeChampService do
let(:params) { ActionController::Parameters.new({ procedure: { types_de_champ_attributes: types_de_champ_attributes } }) }
let(:procedure) { create(:procedure) }
let(:service) { TypesDeChampService.new(procedure) }
describe 'create_update_procedure_params' do
let(:result) { service.create_update_procedure_params(params) }
describe 'the drop down list attributes' do
let(:types_de_champ_attributes) do
{
"0": {
libelle: 'top',
drop_down_list_attributes: {
value: "un\r\n deux\r\n -- commentaire --\r\n trois",
id: '5218'
}
}
}
end
subject { result['types_de_champ_attributes']['0']['drop_down_list_attributes'] }
it 'has its value stripped' do
expect(subject['value']).to eq("un\r\ndeux\r\n-- commentaire --\r\ntrois")
end
end
describe 'reorder the fields' do
let(:types_de_champ_attributes) do
{
'0': { 'libelle': 'a', 'order_place': '0', 'custom_order_place': '1' },
'1': { 'libelle': 'b', 'order_place': '1', 'custom_order_place': '2' }
}
end
subject { result['types_de_champ_attributes'].to_unsafe_hash }
it do
is_expected.to match({
'0': { 'libelle': 'a', 'order_place': '0', 'private': false },
'1': { 'libelle': 'b', 'order_place': '1', 'private': false }
})
end
context 'when the user specifies a position on one element' do
let(:types_de_champ_attributes) do
{
'0': { 'libelle': 'a', 'order_place': '1', 'custom_order_place': '1' },
'1': { 'libelle': 'b', 'order_place': '10', 'custom_order_place': '10' },
'2': { 'libelle': 'c', 'order_place': '11', 'custom_order_place': '2' }
}
end
it do
is_expected.to match({
'0': { 'libelle': 'a', 'order_place': '0', 'private': false },
'1': { 'libelle': 'c', 'order_place': '1', 'private': false },
'2': { 'libelle': 'b', 'order_place': '2', 'private': false }
})
end
end
context 'when the user puts a champ down' do
let(:types_de_champ_attributes) do
{
'0': { 'libelle': 'a', 'order_place': '0', 'custom_order_place': '2' },
'1': { 'libelle': 'b', 'order_place': '1', 'custom_order_place': '2' },
'2': { 'libelle': 'c', 'order_place': '2', 'custom_order_place': '3' }
}
end
it do
is_expected.to match({
'0': { 'libelle': 'b', 'order_place': '0', 'private': false },
'1': { 'libelle': 'a', 'order_place': '1', 'private': false },
'2': { 'libelle': 'c', 'order_place': '2', 'private': false }
})
end
end
context 'when the user uses not a number' do
let(:types_de_champ_attributes) do
{
'0': { 'libelle': 'a', 'order_place': '0', 'custom_order_place': '1' },
'1': { 'libelle': 'b', 'order_place': '1', 'custom_order_place': '2' },
'2': { 'libelle': 'c', 'order_place': '2', 'custom_order_place': '' },
'3': { 'libelle': 'd', 'order_place': '3', 'custom_order_place': 'a' }
}
end
it 'does not change the natural order' do
is_expected.to match({
'0': { 'libelle': 'a', 'order_place': '0', 'private': false },
'1': { 'libelle': 'b', 'order_place': '1', 'private': false },
'2': { 'libelle': 'c', 'order_place': '2', 'private': false },
'3': { 'libelle': 'd', 'order_place': '3', 'private': false }
})
end
end
end
end
describe ".options" do
let(:pj_option) { ["Pièce justificative", TypeDeChamp.type_champs.fetch(:piece_justificative)] }
subject { service.options }
it { is_expected.to include(pj_option) }
end
end

View file

@ -1,53 +0,0 @@
require 'spec_helper'
describe 'admin/types_de_champ/show.html.haml', type: :view do
let(:procedure) { create(:procedure) }
# FIXME: delete this when support for pj champ is generalized
before { allow(view).to receive(:current_administrateur).and_return(create(:administrateur)) }
describe 'fields sorted' do
let(:first_libelle) { 'salut la compagnie' }
let(:last_libelle) { 'je suis bien sur la page' }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1, libelle: last_libelle) }
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0, libelle: first_libelle) }
before do
procedure.reload
assign(:procedure, procedure)
assign(:type_de_champ_service, TypesDeChampService.new(procedure))
render
end
it 'sorts by order place' do
expect(rendered).to match(/#{first_libelle}.*#{last_libelle}/m)
end
end
describe 'arrow button' do
subject do
procedure.reload
assign(:procedure, procedure)
assign(:type_de_champ_service, TypesDeChampService.new(procedure))
render
rendered
end
context 'when there is no field in database' do
it { expect(subject).not_to have_css('.fa-chevron-down') }
it { expect(subject).not_to have_css('.fa-chevron-up') }
end
context 'when there is only one field in database' do
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
it { expect(subject).not_to have_css('#btn_down_0') }
it { expect(subject).not_to have_css('#btn_up_0') }
it { expect(subject).not_to have_css('#btn_up_1') }
it { expect(subject).not_to have_css('#btn_down_1') }
end
context 'when there are 2 fields in database' do
let!(:type_de_champ_0) { create(:type_de_champ, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, procedure: procedure, order_place: 1) }
it { expect(subject).not_to have_css('#btn_up_0') }
it { expect(subject).to have_css('#btn_down_0') }
it { expect(subject).to have_css('#btn_up_1') }
it { expect(subject).not_to have_css('#btn_down_1') }
end
end
end

View file

@ -1,62 +0,0 @@
require 'spec_helper'
describe 'admin/types_de_champ/show.html.haml', type: :view do
let(:procedure) { create(:procedure) }
# FIXME: delete this when support for pj champ is generalized
before { allow(view).to receive(:current_administrateur).and_return(create(:administrateur)) }
describe 'fields sorted' do
let(:first_libelle) { 'salut la compagnie' }
let(:last_libelle) { 'je suis bien sur la page' }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1, libelle: last_libelle) }
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0, libelle: first_libelle) }
before do
procedure.reload
assign(:procedure, procedure)
assign(:type_de_champ_service, TypesDeChampService.new(procedure, true))
render
end
it 'sorts by order place' do
expect(rendered).to match(/#{first_libelle}.*#{last_libelle}/m)
end
end
describe 'elements presents or not' do
subject do
procedure.reload
assign(:procedure, procedure)
assign(:type_de_champ_service, TypesDeChampService.new(procedure, true))
render
rendered
end
describe 'mandatory checkbox' do
it 'no mandatory checkbox are present' do
expect(subject).to have_css('.form-group.mandatory[style*="visibility: hidden"]')
end
end
describe 'arrow button' do
context 'when there is no field in database' do
it { expect(subject).not_to have_css('.fa-chevron-down') }
it { expect(subject).not_to have_css('.fa-chevron-up') }
end
context 'when there is only one field in database' do
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
it { expect(subject).not_to have_css('#btn_down_0') }
it { expect(subject).not_to have_css('#btn_up_0') }
it { expect(subject).not_to have_css('#btn_up_1') }
it { expect(subject).not_to have_css('#btn_down_1') }
end
context 'when there are 2 fields in database' do
let!(:type_de_champ_0) { create(:type_de_champ, :private, procedure: procedure, order_place: 0) }
let!(:type_de_champ_1) { create(:type_de_champ, :private, procedure: procedure, order_place: 1) }
it { expect(subject).to have_css('#btn_down_0') }
it { expect(subject).not_to have_css('#btn_up_0') }
it { expect(subject).to have_css('#btn_up_1') }
it { expect(subject).not_to have_css('#btn_down_1') }
end
end
end
end