Merge branch 'main' into fix/stored_query_issue

This commit is contained in:
Damien Le Thiec 2023-03-01 10:22:40 +01:00 committed by GitHub
commit 8a7cb3f1fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
301 changed files with 4456 additions and 1168 deletions

View file

@ -103,7 +103,7 @@ RSpec.describe Attachment::EditComponent, type: :component do
it 'displays the filename, but doesnt allow to download the file' do
expect(attachment.watermark_pending?).to be_truthy
expect(subject).to have_text(filename)
expect(subject).to have_link('Supprimer')
expect(subject).to have_button('Supprimer')
expect(subject).to have_no_link(text: filename) # don't match "Delete" link which also include filename in title attribute
expect(subject).to have_text('Traitement en cours')
end
@ -152,8 +152,12 @@ RSpec.describe Attachment::EditComponent, type: :component do
end
end
context 'when the file is scanned and safe' do
context 'when the file is scanned, watermarked_at, and viewed as download and safe' do
let(:kwargs) { { view_as: :download } }
let(:virus_scan_result) { ActiveStorage::VirusScanner::SAFE }
before do
attachment.blob.touch(:watermarked_at)
end
it 'allows to download the file' do
expect(subject).to have_link(filename)

View file

@ -47,8 +47,8 @@ RSpec.describe Attachment::MultipleComponent, type: :component do
end
it 'shows the Delete button by default' do
expect(subject).to have_link(title: "Supprimer le fichier #{attached_file.attachments[0].filename}")
expect(subject).to have_link(title: "Supprimer le fichier #{attached_file.attachments[1].filename}")
expect(subject).to have_button(title: "Supprimer le fichier #{attached_file.attachments[0].filename}")
expect(subject).to have_button(title: "Supprimer le fichier #{attached_file.attachments[1].filename}")
end
it 'renders a form field for uploading a new file' do

View file

@ -0,0 +1,73 @@
describe SimpleFormatComponent, type: :component do
let(:allow_a) { false }
before { render_inline(described_class.new(text, allow_a: allow_a)) }
context 'one line' do
let(:text) do
"1er paragraphe"
end
it { expect(page).to have_selector("p", count: 1, text: text) }
end
context 'one with leading spaces' do
let(:text) do
<<-TEXT
1er paragraphe
TEXT
end
it { expect(page).to have_selector("p", count: 1, text: text.strip) }
end
context 'two lines' do
let(:text) do
<<~TEXT
1er paragraphe
2eme paragraphe
TEXT
end
it { expect(page).to have_selector("p", count: 2) }
it { text.split("\n").map(&:strip).map { expect(page).to have_text(_1) } }
end
context 'unordered list items' do
let(:text) do
<<~TEXT
- 1er paragraphe
- paragraphe
TEXT
end
it { expect(page).to have_selector("ul", count: 1) }
it { expect(page).to have_selector("li", count: 2) }
end
context 'ordered list items' do
let(:text) do
<<~TEXT
1. 1er paragraphe
2. paragraphe
TEXT
end
it { expect(page).to have_selector("ol", count: 1) }
it { expect(page).to have_selector("li", count: 2) }
end
context 'auto-link' do
let(:text) do
<<~TEXT
bonjour https://www.demarches-simplifiees.fr
TEXT
end
context 'enabled' do
let(:allow_a) { true }
it { expect(page).to have_selector("a") }
end
context 'disabled' do
it { expect(page).not_to have_selector("a") }
end
end
end

View file

@ -268,11 +268,22 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
context 'of a news instructeurs' do
let(:new_instructeur_emails) { ['new_i1@mail.com', 'new_i2@mail.com'] }
before { do_request }
before do
allow(GroupeInstructeurMailer).to receive(:notify_added_instructeurs)
.and_return(double(deliver_later: true))
do_request
end
it { expect(gi_1_2.instructeurs.pluck(:email)).to include(*new_instructeur_emails) }
it { expect(flash.notice).to be_present }
it { expect(response).to redirect_to(admin_procedure_groupe_instructeur_path(procedure, gi_1_2)) }
it { expect(procedure.routing_enabled?).to be_truthy }
it "calls GroupeInstructeurMailer with the right params" do
expect(GroupeInstructeurMailer).to have_received(:notify_added_instructeurs).with(
gi_1_2,
gi_1_2.instructeurs.last(2),
admin.email
)
end
end
context 'of an instructeur already in the group' do
@ -320,11 +331,22 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
end
context 'when there are many instructeurs' do
before { remove_instructeur(admin.instructeur) }
before do
allow(GroupeInstructeurMailer).to receive(:notify_removed_instructeur)
.and_return(double(deliver_later: true))
remove_instructeur(admin.instructeur)
end
it { expect(gi_1_1.instructeurs).to include(instructeur) }
it { expect(gi_1_1.reload.instructeurs.count).to eq(1) }
it { expect(response).to redirect_to(admin_procedure_groupe_instructeur_path(procedure, gi_1_1)) }
it "calls GroupeInstructeurMailer with the right groupe and instructeur" do
expect(GroupeInstructeurMailer).to have_received(:notify_removed_instructeur).with(
gi_1_1,
admin.instructeur,
admin.email
)
end
end
context 'when there is only one instructeur' do
@ -383,6 +405,17 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
it { expect(flash.alert).to eq("Import terminé. Cependant les emails suivants ne sont pas pris en compte: kara") }
end
context 'when the csv file has only one column' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/valid-instructeurs-file.csv', 'text/csv') }
before { subject }
it { expect { subject }.not_to raise_error }
it { expect(response.status).to eq(302) }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq("Importation impossible, veuillez importer un csv <a href=\"/csv/#{I18n.locale}/import-groupe-test.csv\">suivant ce modèle</a>") }
end
context 'when the file content type is application/vnd.ms-excel' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe_avec_caracteres_speciaux.csv', "application/vnd.ms-excel") }
@ -395,11 +428,16 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
context 'when the content of csv contains special characters' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe_avec_caracteres_speciaux.csv', 'text/csv') }
before { subject }
before do
allow(GroupeInstructeurMailer).to receive(:notify_added_instructeurs)
.and_return(double(deliver_later: true))
subject
end
it { expect(procedure.groupe_instructeurs.pluck(:label)).to match_array(["Auvergne-Rhône-Alpes", "Vendée", "défaut"]) }
it { expect(flash.notice).to be_present }
it { expect(flash.notice).to eq("La liste des instructeurs a été importée avec succès") }
it { expect(GroupeInstructeurMailer).to have_received(:notify_added_instructeurs).twice }
end
context 'when the csv file length is more than 1 mo' do
@ -443,6 +481,76 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
end
end
describe '#add_instructeurs_via_csv_file' do
let(:procedure_non_routee) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
subject do
post :import, params: { procedure_id: procedure_non_routee.id, instructeurs_csv_file: csv_file }
end
context 'when the csv file is less than 1 mo and content type text/csv' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/instructeurs-file.csv', 'text/csv') }
before do
allow(GroupeInstructeurMailer).to receive(:notify_added_instructeurs)
.and_return(double(deliver_later: true))
subject
end
it { expect(response.status).to eq(302) }
it { expect(procedure_non_routee.instructeurs.pluck(:email)).to match_array(["kara@beta-gouv.fr", "philippe@mail.com", "lisa@gouv.fr"]) }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq("Import terminé. Cependant les emails suivants ne sont pas pris en compte: eric") }
it "calls GroupeInstructeurMailer" do
expect(GroupeInstructeurMailer).to have_received(:notify_added_instructeurs).with(
procedure_non_routee.defaut_groupe_instructeur,
any_args,
admin.email
)
end
end
context 'when the csv file has more than one column' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe-instructeur.csv', 'text/csv') }
before { subject }
it { expect(response.status).to eq(302) }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq("Importation impossible, veuillez importer un csv <a href=\"/csv/import-instructeurs-test.csv\">suivant ce modèle</a>") }
end
context 'when the file content type is application/vnd.ms-excel' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/valid-instructeurs-file.csv', "application/vnd.ms-excel") }
before { subject }
it { expect(procedure_non_routee.instructeurs.pluck(:email)).to match_array(["kara@beta-gouv.fr", "philippe@mail.com", "lisa@gouv.fr"]) }
it { expect(flash.notice).to be_present }
it { expect(flash.notice).to eq("La liste des instructeurs a été importée avec succès") }
end
context 'when the csv file length is more than 1 mo' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/groupe-instructeur.csv', 'text/csv') }
before do
allow_any_instance_of(ActionDispatch::Http::UploadedFile).to receive(:size).and_return(3.megabytes)
subject
end
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq("Importation impossible : le poids du fichier est supérieur à 1 Mo") }
end
context 'when the file content type is not accepted' do
let(:csv_file) { fixture_file_upload('spec/fixtures/files/french-flag.gif', 'image/gif') }
before { subject }
it { expect(flash.alert).to be_present }
it { expect(flash.alert).to eq("Importation impossible : veuillez importer un fichier CSV") }
end
end
describe '#export_groupe_instructeurs' do
let(:procedure) { create(:procedure, :published) }
let(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 1 2') }

View file

@ -132,6 +132,16 @@ describe Administrateurs::ProceduresController, type: :controller do
expect(assigns(:procedures).any? { |p| p.id == procedure2.id }).to be_truthy
expect(assigns(:procedures).any? { |p| p.id == procedure1.id }).to be_falsey
end
context "without zones" do
let!(:procedure) { create(:procedure, :published, zones: []) }
subject { get :all }
it 'displays procedures without zones' do
subject
expect(assigns(:procedures).any? { |p| p.id == procedure.id }).to be_truthy
end
end
end
context 'for specific status' do

View file

@ -50,8 +50,8 @@ RSpec.describe API::Public::V1::DossiersController, type: :controller do
let(:params) {
{
id: procedure.id,
"champ_#{type_de_champ_1.to_typed_id}" => value_1,
"champ_#{type_de_champ_2.to_typed_id}" => value_2
"champ_#{type_de_champ_1.to_typed_id_for_query}" => value_1,
"champ_#{type_de_champ_2.to_typed_id_for_query}" => value_2
}
}
@ -130,6 +130,74 @@ RSpec.describe API::Public::V1::DossiersController, type: :controller do
end
end
describe '#index' do
let(:procedure) { dossier.procedure }
let(:dossier) { create(:dossier, prefilled: true, user: nil) }
let(:prefill_token) { dossier.prefill_token }
let(:params) { { id: procedure.id, prefill_token: } }
subject(:create_request) do
request.headers["Content-Type"] = "application/json"
get :index, params:
end
let(:body) { JSON.parse(response.body).map(&:deep_symbolize_keys) }
before { create_request }
context 'not found' do
let(:prefill_token) { 'invalid_token' }
it 'should respond with and empty array' do
expect(response).to have_http_status(:ok)
expect(body).to eq([])
end
end
context 'when dossier prefilled' do
it 'should respond with dossier state' do
expect(response).to have_http_status(:ok)
expect(body.first[:state]).to eq('prefilled')
end
end
context 'when dossier brouillon' do
let(:dossier) { create(:dossier, prefilled: true) }
it 'should respond with dossier state' do
expect(response).to have_http_status(:ok)
expect(body.first[:state]).to eq('brouillon')
end
end
context 'when dossier en_construction' do
let(:dossier) { create(:dossier, :en_construction, prefilled: true) }
it 'should respond with dossier state' do
expect(response).to have_http_status(:ok)
expect(body.first[:state]).to eq('en_construction')
expect(body.first[:submitted_at]).to eq(dossier.depose_at.iso8601)
end
end
context 'with multiple tokens' do
let(:dossier) { create(:dossier, prefilled: true, user: nil) }
let(:other_dossier) { create(:dossier, prefilled: true, user: nil, procedure:) }
let(:prefill_token) { [dossier.prefill_token, other_dossier.prefill_token] }
it 'should respond with dossiers state' do
expect(response).to have_http_status(:ok)
expect(body.map { _1[:dossier_number] }).to match_array([dossier.id, other_dossier.id])
end
context 'comma separated tokens' do
let(:prefill_token) { [dossier.prefill_token, other_dossier.prefill_token].join(',') }
it 'should respond with dossiers state' do
expect(response).to have_http_status(:ok)
expect(body.map { _1[:dossier_number] }).to match_array([dossier.id, other_dossier.id])
end
end
end
end
private
def find_champ_by_stable_id(dossier, stable_id)

View file

@ -4,6 +4,13 @@ describe API::V1::DossiersController do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, administrateur: admin) }
let(:wrong_procedure) { create(:procedure) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
it { expect(described_class).to be < APIController }
describe 'GET index (with bearer token)' do
@ -258,7 +265,7 @@ describe API::V1::DossiersController do
end
end
describe 'departement' do
describe 'departement', vcr: { cassette_name: 'api_geo_departements' } do
let(:procedure) { create(:procedure, :with_departement, administrateur: admin) }
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure: procedure) }
@ -279,9 +286,9 @@ describe API::V1::DossiersController do
it 'should have rows' do
expect(subject.size).to eq(2)
expect(subject[0][:id]).to eq(1)
expect(subject[0][:champs].size).to eq(1)
expect(subject[0][:champs].map { |c| c[:value] }).to eq(['text'])
expect(subject[0][:champs].map { |c| c[:type_de_champ][:type_champ] }).to eq(['text'])
expect(subject[0][:champs].size).to eq(2)
expect(subject[0][:champs].map { |c| c[:value] }).to eq(['text', 42])
expect(subject[0][:champs].map { |c| c[:type_de_champ][:type_champ] }).to eq(['text', 'integer_number'])
end
end
end

View file

@ -42,7 +42,6 @@ describe API::V2::GraphqlController do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
allow(APIGeoService).to receive(:departement_name).with('01').and_return('Ain')
instructeur.assign_to_procedure(procedure)
end
@ -397,7 +396,7 @@ describe API::V2::GraphqlController do
dossier
end
context "for individual", vcr: { cassette_name: 'api_geo_regions' } do
context "for individual", vcr: { cassette_name: 'api_geo_all' } do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) }
let(:query) do
"{

View file

@ -138,6 +138,7 @@ describe API::V2::GraphqlController do
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarcheDescriptor][:id]).to eq(procedure.to_typed_id)
expect(gql_data[:demarcheDescriptor][:demarcheUrl]).to match("commencer/#{procedure.path}")
}
end

View file

@ -37,10 +37,8 @@ describe Champs::RNAController, type: :controller do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the data and value on the model' do
champ.reload
expect(champ.data).to eq({})
expect(champ.value).to eq("")
it 'clears the data on the model' do
expect(champ.reload.data).to be_nil
end
it 'clears any information or error message' do
@ -55,14 +53,12 @@ describe Champs::RNAController, type: :controller do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the data and value on the model' do
champ.reload
expect(champ.data).to be_nil
expect(champ.value).to be_nil
it 'clears the data on the model' do
expect(champ.reload.data).to be_nil
end
it 'displays a “RNA is invalid” error message' do
expect(response.body).to include("Aucun établissement trouvé")
expect(response.body).to include("Le numéro RNA doit commencer par un W majuscule suivi de 9 chiffres")
end
end
@ -75,7 +71,7 @@ describe Champs::RNAController, type: :controller do
it 'clears the data on the model' do
champ.reload
expect(champ.data).to eq({})
expect(champ.data).to be_nil
end
it 'displays a “RNA is invalid” error message' do
@ -94,10 +90,8 @@ describe Champs::RNAController, type: :controller do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the data and value on the model' do
champ.reload
expect(champ.data).to be_nil
expect(champ.value).to be_nil
it 'clears the data on the model' do
expect(champ.reload.data).to be_nil
end
it 'displays a “API is unavailable” error message' do

View file

@ -31,6 +31,8 @@ describe Champs::SiretController, type: :controller do
sign_in user
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/)
.to_return(status: api_etablissement_status, body: api_etablissement_body)
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siret[0..8]}/)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/entreprises.json'))
allow_any_instance_of(APIEntrepriseToken).to receive(:roles)
.and_return(["attestations_fiscales", "attestations_sociales", "bilans_entreprise_bdf"])
allow_any_instance_of(APIEntrepriseToken).to receive(:expired?).and_return(token_expired)
@ -39,10 +41,8 @@ describe Champs::SiretController, type: :controller do
context 'when the SIRET is empty' do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the etablissement and SIRET on the model' do
champ.reload
expect(champ.etablissement).to be_nil
expect(champ.value).to be_empty
it 'clears the etablissement on the model' do
expect(champ.reload.etablissement).to be_nil
end
it 'clears any information or error message' do
@ -50,15 +50,13 @@ describe Champs::SiretController, type: :controller do
end
end
context 'when the SIRET is invalid' do
context "when the SIRET is invalid because of it's length" do
let(:siret) { '1234' }
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the etablissement and SIRET on the model' do
champ.reload
expect(champ.etablissement).to be_nil
expect(champ.value).to be_empty
it 'clears the etablissement on the model' do
expect(champ.reload.etablissement).to be_nil
end
it 'displays a “SIRET is invalid” error message' do
@ -66,6 +64,20 @@ describe Champs::SiretController, type: :controller do
end
end
context "when the SIRET is invalid because of it's checksum" do
let(:siret) { '82812345600023' }
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the etablissement on the model' do
expect(champ.reload.etablissement).to be_nil
end
it 'displays a “SIRET is invalid” error message' do
expect(response.body).to include('Le format du numéro de SIRET est invalide.')
end
end
context 'when the API is unavailable due to network error' do
let(:siret) { '82161143100015' }
let(:api_etablissement_status) { 503 }
@ -76,10 +88,8 @@ describe Champs::SiretController, type: :controller do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the etablissement and SIRET on the model' do
champ.reload
expect(champ.etablissement).to be_nil
expect(champ.value).to be_empty
it 'clears the etablissement on the model' do
expect(champ.reload.etablissement).to be_nil
end
it 'displays a “API is unavailable” error message' do
@ -115,10 +125,8 @@ describe Champs::SiretController, type: :controller do
subject! { get :show, params: params, format: :turbo_stream }
it 'clears the etablissement and SIRET on the model' do
champ.reload
expect(champ.etablissement).to be_nil
expect(champ.value).to be_empty
it 'clears the etablissement on the model' do
expect(champ.reload.etablissement).to be_nil
end
it 'displays a “SIRET not found” error message' do

View file

@ -792,7 +792,7 @@ describe Instructeurs::DossiersController, type: :controller do
champs_private_attributes: {
'0': {
id: champ_multiple_drop_down_list.id,
value: ['', 'un', 'deux']
value: ['', 'val1', 'val2']
},
'1': {
id: champ_datetime.id,
@ -813,7 +813,7 @@ describe Instructeurs::DossiersController, type: :controller do
end
it {
expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]')
expect(champ_multiple_drop_down_list.value).to eq('["val1", "val2"]')
expect(champ_linked_drop_down_list.primary_value).to eq('primary')
expect(champ_linked_drop_down_list.secondary_value).to eq('secondary')
expect(champ_datetime.value).to eq('2019-12-21T13:17:00+01:00')
@ -839,7 +839,7 @@ describe Instructeurs::DossiersController, type: :controller do
champs_public_attributes: {
'0': {
id: champ_multiple_drop_down_list.id,
value: ['', 'un', 'deux']
value: ['', 'val1', 'val2']
}
}
}

View file

@ -27,8 +27,7 @@ describe PasswordComplexityController, type: :controller do
it 'renders Javascript that updates the password complexity meter' do
subject
expect(response.body).to include('complexity-label')
expect(response.body).to include('complexity-bar')
expect(response.body).to include('Mot de passe vulnérable')
end
end
end

View file

@ -67,24 +67,19 @@ describe PrefillDescriptionsController, type: :controller do
it { expect(response).to render_template(:update) }
it "includes the prefill URL" do
type_de_champ_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
type_de_champ_to_add_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ_to_add.type_champ}")
expect(response.body).to include(commencer_path(path: procedure.path))
expect(response.body).to include(
{
"champ_#{type_de_champ.to_typed_id}" => I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
}.to_query
)
expect(response.body).to include({
"champ_#{type_de_champ_to_add.to_typed_id}" => I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ_to_add.type_champ}")
}.to_query)
expect(response.body).to include("champ_#{type_de_champ.to_typed_id_for_query}=#{type_de_champ_value}")
expect(response.body).to include("champ_#{type_de_champ_to_add.to_typed_id_for_query}=#{type_de_champ_to_add_value}")
end
it "includes the prefill query" do
type_de_champ_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
type_de_champ_to_add_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ_to_add.type_champ}")
expect(response.body).to include(api_public_v1_dossiers_path(procedure))
expect(response.body).to include(
"&quot;champ_#{type_de_champ.to_typed_id}&quot;: &quot;#{type_de_champ_value}&quot;, &quot;champ_#{type_de_champ_to_add.to_typed_id}&quot;: &quot;#{type_de_champ_to_add_value}&quot"
"&quot;champ_#{type_de_champ.to_typed_id_for_query}&quot;:&quot;#{type_de_champ_value}&quot;,&quot;champ_#{type_de_champ_to_add.to_typed_id_for_query}&quot;:&quot;#{type_de_champ_to_add_value}&quot"
)
end
end
@ -96,13 +91,11 @@ describe PrefillDescriptionsController, type: :controller do
it { expect(response).to render_template(:update) }
it "includes the prefill URL" do
type_de_champ_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
type_de_champ_to_remove_value = I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ_to_remove.type_champ}")
expect(response.body).to include(commencer_path(path: procedure.path))
expect(response.body).to include({
"champ_#{type_de_champ.to_typed_id}" => I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
}.to_query)
expect(response.body).not_to include({
"champ_#{type_de_champ_to_remove.to_typed_id}" => I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ_to_remove.type_champ}")
}.to_query)
expect(response.body).to include("champ_#{type_de_champ.to_typed_id_for_query}=#{type_de_champ_value}")
expect(response.body).not_to include("champ_#{type_de_champ_to_remove.to_typed_id_for_query}=#{type_de_champ_to_remove_value}")
end
it "includes the prefill query" do
@ -111,10 +104,10 @@ describe PrefillDescriptionsController, type: :controller do
expect(response.body).to include(api_public_v1_dossiers_path(procedure))
expect(response.body).to include(
"&quot;champ_#{type_de_champ.to_typed_id}&quot;: &quot;#{type_de_champ_value}&quot;"
"&quot;champ_#{type_de_champ.to_typed_id_for_query}&quot;:&quot;#{type_de_champ_value}&quot;"
)
expect(response.body).not_to include(
"&quot;champ_#{type_de_champ_to_remove.to_typed_id}&quot;: &quot;#{type_de_champ_to_remove_value}&quot;"
"&quot;champ_#{type_de_champ_to_remove.to_typed_id_for_query}&quot;:&quot;#{type_de_champ_to_remove_value}&quot;"
)
end
end

View file

@ -8,6 +8,8 @@ RSpec.describe PrefillTypeDeChampsController, type: :controller do
context 'when the procedure is found' do
context 'when the procedure is publiee' do
context 'when the procedure is opendata' do
render_views
let(:procedure) { create(:procedure, :published, opendata: true) }
it { expect(show_request).to render_template(:show) }

View file

@ -196,6 +196,8 @@ describe Users::DossiersController, type: :controller do
sign_in(user)
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/)
.to_return(status: api_etablissement_status, body: api_etablissement_body)
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siren}/)
.to_return(body: File.read('spec/fixtures/files/api_entreprise/entreprises.json'), status: 200)
allow_any_instance_of(APIEntrepriseToken).to receive(:roles)
.and_return(["attestations_fiscales", "attestations_sociales", "bilans_entreprise_bdf"])
allow_any_instance_of(APIEntrepriseToken).to receive(:expired?).and_return(token_expired)
@ -254,7 +256,7 @@ describe Users::DossiersController, type: :controller do
context 'When API-Entreprise is globally down' do
let(:api_etablissement_status) { 502 }
let(:api_current_status_response) { File.read('spec/fixtures/files/api_entreprise/current_status.json').tr('200', '502') }
let(:api_current_status_response) { File.read('spec/fixtures/files/api_entreprise/current_status.json').gsub('200', '502') }
it "create an etablissement only with SIRET as degraded mode" do
dossier.reload

View file

@ -97,7 +97,7 @@ FactoryBot.define do
factory :champ_multiple_drop_down_list, class: 'Champs::MultipleDropDownListChamp' do
type_de_champ { association :type_de_champ_multiple_drop_down_list, procedure: dossier.procedure }
value { '["choix 1", "choix 2"]' }
value { '["val1", "val2"]' }
end
factory :champ_linked_drop_down_list, class: 'Champs::LinkedDropDownListChamp' do
@ -122,7 +122,9 @@ FactoryBot.define do
factory :champ_communes, class: 'Champs::CommuneChamp' do
type_de_champ { association :type_de_champ_communes, procedure: dossier.procedure }
value { 'Paris' }
value { 'Coye-la-Forêt (60580)' }
value_json { { "departement" => "Oise", "code_departement" => "60" } }
external_id { "60172" }
end
factory :champ_epci, class: 'Champs::EpciChamp' do

View file

@ -206,6 +206,12 @@ FactoryBot.define do
end
end
trait :with_region do
after(:build) do |procedure, _evaluator|
build(:type_de_champ_regions, procedure: procedure)
end
end
trait :with_piece_justificative do
after(:build) do |procedure, _evaluator|
build(:type_de_champ_piece_justificative, procedure: procedure)

View file

@ -198,6 +198,16 @@ FactoryBot.define do
parent = revision.revision_types_de_champ.find { |rtdc| rtdc.type_de_champ == type_de_champ_repetition }
build(:type_de_champ, procedure: evaluator.procedure, libelle: 'sub type de champ', parent: parent, position: 0)
build(:type_de_champ, type_champ: TypeDeChamp.type_champs.fetch(:integer_number), procedure: evaluator.procedure, libelle: 'sub type de champ2', parent: parent, position: 1)
end
end
trait :with_region_types_de_champ do
after(:build) do |type_de_champ_repetition, evaluator|
revision = evaluator.procedure.active_revision
parent = revision.revision_types_de_champ.find { |rtdc| rtdc.type_de_champ == type_de_champ_repetition }
build(:type_de_champ, type_champ: TypeDeChamp.type_champs.fetch(:regions), procedure: evaluator.procedure, libelle: 'region sub_champ', parent: parent, position: 10)
end
end
end

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,14 @@
{
"nhits": 0,
"parameters": {
"dataset": "fr-en-annuaire-education",
"rows": 1,
"start": 0,
"refine": {
"identifiant_de_l_etablissement": "0341247"
},
"format": "json",
"timezone": "UTC"
},
"records": []
}

View file

@ -0,0 +1,5 @@
Email
kara@beta-gouv.fr
philippe@mail.com
lisa@gouv.fr
eric
1 Email
2 kara@beta-gouv.fr
3 philippe@mail.com
4 lisa@gouv.fr
5 eric

View file

@ -0,0 +1,4 @@
Email
kara@beta-gouv.fr
philippe@mail.com
lisa@gouv.fr
1 Email
2 kara@beta-gouv.fr
3 philippe@mail.com
4 lisa@gouv.fr

View file

@ -8,6 +8,24 @@ RSpec.describe Types::DemarcheType, type: :graphql do
let(:data) { subject['data'].deep_symbolize_keys }
let(:errors) { subject['errors'].deep_symbolize_keys }
describe 'context should correctly preserve demarche authorization state' do
let(:query) { DEMARCHE_QUERY }
let(:admin) { create(:administrateur) }
let(:procedure) { create(:procedure, administrateurs: [admin]) }
let(:other_admin_procedure) { create(:procedure) }
let(:context) { { administrateur_id: admin.id } }
let(:variables) { { number: procedure.id } }
it do
result = API::V2::Schema.execute(query, variables: variables, context: context)
graphql_context = result.context
expect(graphql_context.authorized_demarche?(procedure)).to be_truthy
expect(graphql_context.authorized_demarche?(other_admin_procedure)).to be_falsey
end
end
describe 'demarche with clone' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :yes_no }]) }
let(:procedure_clone) { procedure.clone(procedure.administrateurs.first, false) }
@ -23,6 +41,13 @@ RSpec.describe Types::DemarcheType, type: :graphql do
expect(procedure.draft_revision.types_de_champ_public.first.stable_id).to eq(procedure_clone.draft_revision.types_de_champ_public.first.stable_id)
}
end
DEMARCHE_QUERY = <<-GRAPHQL
query($number: Int!) {
demarche(number: $number) {
number
}
}
GRAPHQL
DEMARCHE_WITH_CHAMP_DESCRIPTORS_QUERY = <<-GRAPHQL
query($number: Int!) {

View file

@ -1,49 +0,0 @@
RSpec.describe StringToHtmlHelper, type: :helper do
describe "#string_to_html" do
let(:allow_a) { false }
subject { string_to_html(description, allow_a:) }
context "with some simple texte" do
let(:description) { "1er ligne \n 2ieme ligne" }
it { is_expected.to eq("<p>1er ligne \n<br> 2ieme ligne</p>") }
end
context "with a link" do
context "using an authorized scheme" do
let(:description) { "Cliquez sur https://d-s.fr pour continuer." }
context 'with a tag authorized' do
let(:allow_a) { true }
it { is_expected.to eq("<p>Cliquez sur <a href=\"https://d-s.fr\" target=\"_blank\" rel=\"noopener\">https://d-s.fr</a> pour continuer.</p>") }
end
context 'without a tag' do
it { is_expected.to eq("<p>Cliquez sur https://d-s.fr pour continuer.</p>") }
end
end
context "using a non-authorized scheme" do
let(:description) { "Cliquez sur file://etc/password pour continuer." }
it { is_expected.to eq("<p>Cliquez sur file://etc/password pour continuer.</p>") }
end
context "not actually an URL" do
let(:description) { "Pour info: il ne devrait y avoir aucun lien." }
it { is_expected.to eq("<p>Pour info: il ne devrait y avoir aucun lien.</p>") }
end
end
context "with empty decription" do
let(:description) { nil }
it { is_expected.to eq nil }
end
context "with a bad script" do
let(:description) { '<script>bad</script>' }
it { is_expected.to eq('<p>bad</p>') }
end
end
end

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ChampFetchExternalDataJob, type: :job do
let(:champ) { Struct.new(:external_id, :data).new(champ_external_id, data) }
let(:external_id) { "an ID" }
let(:champ_external_id) { "an ID" }
let(:data) { nil }
let(:fetched_data) { nil }
subject(:perform_job) { described_class.perform_now(champ, external_id) }
before do
allow(champ).to receive(:fetch_external_data).and_return(fetched_data)
allow(champ).to receive(:update_with_external_data!)
end
shared_examples "a champ non-updater" do
it 'does not update the champ' do
perform_job
expect(champ).not_to have_received(:update_with_external_data!)
end
end
context 'when external_id matches the champ external_id and the champ data is nil' do
it 'fetches external data' do
perform_job
expect(champ).to have_received(:fetch_external_data)
end
context 'when the fetched data is present' do
let(:fetched_data) { "data" }
it 'updates the champ' do
perform_job
expect(champ).to have_received(:update_with_external_data!).with(data: fetched_data)
end
end
context 'when the fetched data is blank' do
it_behaves_like "a champ non-updater"
end
end
context 'when external_id does not match the champ external_id' do
let(:champ_external_id) { "something else" }
it_behaves_like "a champ non-updater"
end
context 'when the champ data is present' do
let(:data) { "present" }
it_behaves_like "a champ non-updater"
end
end

View file

@ -38,4 +38,13 @@ describe APIEducation::AnnuaireEducationAdapter do
expect { subject }.to raise_exception(APIEducation::AnnuaireEducationAdapter::InvalidSchemaError)
end
end
context "when responds with empty schema" do
let(:body) { File.read('spec/fixtures/files/api_education/annuaire_education_empty.json') }
let(:status) { 200 }
it '#to_params returns nil' do
expect(subject).to eq(nil)
end
end
end

View file

@ -21,9 +21,9 @@ describe '20220705164551_remove_unused_champs' do
describe 'remove_unused_champs', vcr: { cassette_name: 'api_geo_all' } do
it "with bad champs" do
expect(Champ.where(dossier: dossier).count).to eq(38)
expect(Champ.where(dossier: dossier).count).to eq(40)
run_task
expect(Champ.where(dossier: dossier).count).to eq(37)
expect(Champ.where(dossier: dossier).count).to eq(39)
end
end
end

View file

@ -1,139 +0,0 @@
describe '20230207144243_normalize_regions', vcr: { cassette_name: 'api_geo_regions' } do
let(:champ) { create(:champ_regions) }
let(:rake_task) { Rake::Task['after_party:normalize_regions'] }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
subject(:run_task) { rake_task.invoke }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
after { rake_task.reenable }
shared_examples "a non-changer" do |external_id, value|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.not_to change { champ.reload.external_id } }
it { expect { run_task }.not_to change { champ.reload.value } }
end
shared_examples "an external_id nullifier" do |external_id, value|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.to change { champ.reload.external_id }.from(external_id).to(nil) }
it { expect { run_task }.not_to change { champ.reload.value } }
end
shared_examples "a value nullifier" do |external_id, value|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.not_to change { champ.reload.external_id } }
it { expect { run_task }.to change { champ.reload.value }.from(value).to(nil) }
end
shared_examples "an external_id and value nullifier" do |external_id, value|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.to change { champ.reload.external_id }.from(external_id).to(nil) }
it { expect { run_task }.to change { champ.reload.value }.from(value).to(nil) }
end
shared_examples "an external_id updater" do |external_id, value, expected_external_id|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.to change { champ.reload.external_id }.from(external_id).to(expected_external_id) }
it { expect { run_task }.not_to change { champ.reload.value } }
end
shared_examples "a result checker" do |external_id, value, expected_external_id, expected_value|
before do
champ.update_columns(external_id:, value:)
run_task
end
it { expect(champ.reload.external_id).to eq(expected_external_id) }
it { expect(champ.reload.value).to eq(expected_value) }
end
shared_examples "a value updater" do |external_id, value, expected_value|
before { champ.update_columns(external_id:, value:) }
it { expect { run_task }.not_to change { champ.reload.external_id } }
it { expect { run_task }.to change { champ.reload.value }.from(value).to(expected_value) }
end
it_behaves_like "a non-changer", nil, nil
it_behaves_like "an external_id nullifier", '', nil
it_behaves_like "a value nullifier", nil, ''
it_behaves_like "an external_id and value nullifier", '', ''
it_behaves_like "an external_id updater", nil, 'Auvergne-Rhône-Alpes', '84'
it_behaves_like "an external_id updater", '', 'Auvergne-Rhône-Alpes', '84'
it_behaves_like "a value updater", '11', nil, 'Île-de-France'
# Integrity data check:
it_behaves_like "a result checker", "84", "Auvergne-Rhône-Alpes", "84", "Auvergne-Rhône-Alpes"
it_behaves_like "a result checker", nil, "Auvergne-Rhône-Alpes", "84", "Auvergne-Rhône-Alpes"
it_behaves_like "a result checker", '', "Auvergne-Rhône-Alpes", "84", "Auvergne-Rhône-Alpes"
it_behaves_like "a result checker", "27", "Bourgogne-Franche-Comté", "27", "Bourgogne-Franche-Comté"
it_behaves_like "a result checker", nil, "Bourgogne-Franche-Comté", "27", "Bourgogne-Franche-Comté"
it_behaves_like "a result checker", '', "Bourgogne-Franche-Comté", "27", "Bourgogne-Franche-Comté"
it_behaves_like "a result checker", "53", "Bretagne", "53", "Bretagne"
it_behaves_like "a result checker", nil, "Bretagne", "53", "Bretagne"
it_behaves_like "a result checker", '', "Bretagne", "53", "Bretagne"
it_behaves_like "a result checker", "24", "Centre-Val de Loire", "24", "Centre-Val de Loire"
it_behaves_like "a result checker", nil, "Centre-Val de Loire", "24", "Centre-Val de Loire"
it_behaves_like "a result checker", '', "Centre-Val de Loire", "24", "Centre-Val de Loire"
it_behaves_like "a result checker", "94", "Corse", "94", "Corse"
it_behaves_like "a result checker", nil, "Corse", "94", "Corse"
it_behaves_like "a result checker", '', "Corse", "94", "Corse"
it_behaves_like "a result checker", "44", "Grand Est", "44", "Grand Est"
it_behaves_like "a result checker", nil, "Grand Est", "44", "Grand Est"
it_behaves_like "a result checker", '', "Grand Est", "44", "Grand Est"
it_behaves_like "a result checker", "01", "Guadeloupe", "01", "Guadeloupe"
it_behaves_like "a result checker", nil, "Guadeloupe", "01", "Guadeloupe"
it_behaves_like "a result checker", '', "Guadeloupe", "01", "Guadeloupe"
it_behaves_like "a result checker", "03", "Guyane", "03", "Guyane"
it_behaves_like "a result checker", nil, "Guyane", "03", "Guyane"
it_behaves_like "a result checker", '', "Guyane", "03", "Guyane"
it_behaves_like "a result checker", "32", "Hauts-de-France", "32", "Hauts-de-France"
it_behaves_like "a result checker", nil, "Hauts-de-France", "32", "Hauts-de-France"
it_behaves_like "a result checker", '', "Hauts-de-France", "32", "Hauts-de-France"
it_behaves_like "a result checker", "04", "La Réunion", "04", "La Réunion"
it_behaves_like "a result checker", nil, "La Réunion", "04", "La Réunion"
it_behaves_like "a result checker", '', "La Réunion", "04", "La Réunion"
it_behaves_like "a result checker", "02", "Martinique", "02", "Martinique"
it_behaves_like "a result checker", nil, "Martinique", "02", "Martinique"
it_behaves_like "a result checker", '', "Martinique", "02", "Martinique"
it_behaves_like "a result checker", "06", "Mayotte", "06", "Mayotte"
it_behaves_like "a result checker", nil, "Mayotte", "06", "Mayotte"
it_behaves_like "a result checker", '', "Mayotte", "06", "Mayotte"
it_behaves_like "a result checker", "28", "Normandie", "28", "Normandie"
it_behaves_like "a result checker", nil, "Normandie", "28", "Normandie"
it_behaves_like "a result checker", '', "Normandie", "28", "Normandie"
it_behaves_like "a result checker", "75", "Nouvelle-Aquitaine", "75", "Nouvelle-Aquitaine"
it_behaves_like "a result checker", nil, "Nouvelle-Aquitaine", "75", "Nouvelle-Aquitaine"
it_behaves_like "a result checker", '', "Nouvelle-Aquitaine", "75", "Nouvelle-Aquitaine"
it_behaves_like "a result checker", "76", "Occitanie", "76", "Occitanie"
it_behaves_like "a result checker", nil, "Occitanie", "76", "Occitanie"
it_behaves_like "a result checker", '', "Occitanie", "76", "Occitanie"
it_behaves_like "a result checker", "52", "Pays de la Loire", "52", "Pays de la Loire"
it_behaves_like "a result checker", nil, "Pays de la Loire", "52", "Pays de la Loire"
it_behaves_like "a result checker", '', "Pays de la Loire", "52", "Pays de la Loire"
it_behaves_like "a result checker", "93", "Provence-Alpes-Côte d'Azur", "93", "Provence-Alpes-Côte dAzur"
it_behaves_like "a result checker", nil, "Provence-Alpes-Côte d'Azur", "93", "Provence-Alpes-Côte dAzur"
it_behaves_like "a result checker", '', "Provence-Alpes-Côte d'Azur", "93", "Provence-Alpes-Côte dAzur"
it_behaves_like "a result checker", "93", "Provence-Alpes-Côte dAzur", "93", "Provence-Alpes-Côte dAzur"
it_behaves_like "a result checker", "11", "Île-de-France", "11", "Île-de-France"
it_behaves_like "a result checker", "11", nil, "11", "Île-de-France"
it_behaves_like "a result checker", nil, "Île-de-France", "11", "Île-de-France"
it_behaves_like "a result checker", '', "Île-de-France", "11", "Île-de-France"
end

View file

@ -1,5 +1,5 @@
RSpec.describe GroupeInstructeurMailer, type: :mailer do
describe '#remove_instructeurs' do
describe '#notify_group_when_instructeurs_removed' do
let(:groupe_instructeur) do
gi = GroupeInstructeur.create(label: 'gi1', procedure: create(:procedure))
gi.instructeurs << create(:instructeur, email: 'int1@g')
@ -7,15 +7,78 @@ RSpec.describe GroupeInstructeurMailer, type: :mailer do
gi.instructeurs << instructeurs_to_remove
gi
end
let(:instructeur_1) { create(:instructeur, email: 'int3@g') }
let(:instructeur_2) { create(:instructeur, email: 'int4@g') }
let(:instructeur_3) { create(:instructeur, email: 'int3@g') }
let(:instructeur_4) { create(:instructeur, email: 'int4@g') }
let(:instructeurs_to_remove) { [instructeur_1, instructeur_2] }
let(:instructeurs_to_remove) { [instructeur_3, instructeur_4] }
let(:current_instructeur_email) { 'toto@email.com' }
subject { described_class.remove_instructeurs(groupe_instructeur, instructeurs_to_remove, current_instructeur_email) }
subject { described_class.notify_group_when_instructeurs_removed(groupe_instructeur, instructeurs_to_remove, current_instructeur_email) }
before { instructeurs_to_remove.each { groupe_instructeur.remove(_1) } }
it { expect(subject.body).to include('Les instructeurs int3@g, int4@g ont été retirés du groupe') }
it { expect(subject.bcc).to match_array(['int1@g', 'int2@g', 'int3@g', 'int4@g']) }
it { expect(subject.bcc).to match_array(['int1@g', 'int2@g']) }
end
describe '#notify_removed_instructeur' do
let(:procedure) { create(:procedure) }
let(:groupe_instructeur) do
gi = GroupeInstructeur.create(label: 'gi1', procedure: procedure)
gi.instructeurs << create(:instructeur, email: 'int1@g')
gi.instructeurs << create(:instructeur, email: 'int2@g')
gi.instructeurs << instructeur_to_remove
gi
end
let(:instructeur_to_remove) { create(:instructeur, email: 'int3@g') }
let(:current_instructeur_email) { 'toto@email.com' }
subject { described_class.notify_removed_instructeur(groupe_instructeur, instructeur_to_remove, current_instructeur_email) }
before { groupe_instructeur.remove(instructeur_to_remove) }
context 'when instructeur is fully removed form procedure' do
it { expect(subject.body).to include('Vous avez été désaffecté(e) de la démarche') }
it { expect(subject.to).to include('int3@g') }
it { expect(subject.to).not_to include('int1@g', 'int2@g') }
end
context 'when instructeur is removed from one group but still affected to procedure' do
let!(:groupe_instructeur_2) do
gi2 = GroupeInstructeur.create(label: 'gi2', procedure: procedure)
gi2.instructeurs << instructeur_to_remove
gi2
end
it { expect(subject.body).to include('Vous avez été retiré(e) du groupe « gi1 » par « toto@email.com »') }
it { expect(subject.to).to include('int3@g') }
it { expect(subject.to).not_to include('int1@g', 'int2@g') }
end
end
describe '#notify_added_instructeurs' do
let(:procedure) { create(:procedure) }
let(:instructeurs_to_add) { [create(:instructeur, email: 'int3@g'), create(:instructeur, email: 'int4@g')] }
let(:current_instructeur_email) { 'toto@email.com' }
subject { described_class.notify_added_instructeurs(procedure.defaut_groupe_instructeur, instructeurs_to_add, current_instructeur_email) }
before { instructeurs_to_add.each { procedure.defaut_groupe_instructeur.add(_1) } }
context 'when there is only one group on procedure' do
it { expect(subject.body).to include('Vous avez été affecté(e) à la démarche') }
it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) }
end
context 'when there are many groups on procedure' do
let!(:groupe_instructeur_2) do
GroupeInstructeur.create(label: 'gi2', procedure: procedure)
end
it { expect(subject.body).to include('Vous avez été ajouté(e) au groupe') }
it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) }
end
end
end

View file

@ -101,4 +101,27 @@ RSpec.describe NotificationMailer, type: :mailer do
end
end
end
describe 'subject length' do
let(:procedure) { create(:simple_procedure, libelle: "My super long title " + ("xo " * 100)) }
let(:dossier) { create(:dossier, :en_instruction, :with_individual, :with_service, user: user, procedure: procedure) }
let(:email_template) { create(:closed_mail, subject:, body: 'Your dossier was accepted. Thanks.') }
before do
dossier.procedure.closed_mail = email_template
end
subject(:mail) { described_class.send_accepte_notification(dossier) }
context "subject is too long" do
let(:subject) { 'Un long libellé --libellé démarche--' }
it { expect(mail.subject.length).to be <= 100 }
end
context "subject should fallback to default" do
let(:subject) { "" }
it { expect(mail.subject).to match(/^Votre dossier .+ a été accepté \(My super long title/) }
it { expect(mail.subject.length).to be <= 100 }
end
end
end

View file

@ -1,9 +1,25 @@
class GroupeInstructeurMailerPreview < ActionMailer::Preview
def remove_instructeurs
def notify_group_when_instructeurs_removed
procedure = Procedure.new(id: 1, libelle: 'une superbe procedure')
groupe = GroupeInstructeur.new(id: 1, label: 'Val-De-Marne', procedure:)
current_instructeur_email = 'admin@dgfip.com'
instructeurs = Instructeur.limit(2)
GroupeInstructeurMailer.remove_instructeurs(groupe, instructeurs, current_instructeur_email)
GroupeInstructeurMailer.notify_group_when_instructeurs_removed(groupe, instructeurs, current_instructeur_email)
end
def notify_removed_instructeur
procedure = Procedure.new(id: 1, libelle: 'une superbe procedure')
groupe = GroupeInstructeur.new(id: 1, label: 'Val-De-Marne', procedure:)
current_instructeur_email = 'admin@dgfip.com'
instructeur = Instructeur.last
GroupeInstructeurMailer.notify_removed_instructeur(groupe, instructeur, current_instructeur_email)
end
def notify_added_instructeurs
procedure = Procedure.new(id: 1, libelle: 'une superbe procedure')
groupe = GroupeInstructeur.new(id: 1, label: 'Val-De-Marne', procedure:)
current_instructeur_email = 'admin@dgfip.com'
instructeurs = Instructeur.limit(2)
GroupeInstructeurMailer.notify_added_instructeurs(groupe, instructeurs, current_instructeur_email)
end
end

View file

@ -117,7 +117,7 @@ describe Champ do
# when using the old form, and the ChampsService Class
# TODO: to remove
context 'when the value is already deserialized' do
let(:value) { '["1", "2"]' }
let(:value) { '["val1", "val2"]' }
it { expect(champ.value).to eq(value) }
@ -133,9 +133,9 @@ describe Champ do
# GOTCHA
context 'when the value is not already deserialized' do
context 'when a choice is selected' do
let(:value) { '["", "1", "2"]' }
let(:value) { '["", "val1", "val2"]' }
it { expect(champ.value).to eq('["1", "2"]') }
it { expect(champ.value).to eq('["val1", "val2"]') }
end
context 'when all choices are removed' do
@ -526,7 +526,7 @@ describe Champ do
expect(dossier.champs_public.size).to eq(2)
expect(champ.rows.size).to eq(2)
second_row = champ.rows.second
second_row = champ.reload.rows.second
expect(second_row.size).to eq(1)
expect(second_row.first.dossier).to eq(dossier)
@ -602,4 +602,12 @@ describe Champ do
end
end
end
describe '#update_with_external_data!' do
let(:champ) { create(:champ_siret) }
let(:data) { "data" }
subject { champ.update_with_external_data!(data: data) }
it { expect { subject }.to change { champ.reload.data }.to(data) }
end
end

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Champs::AnnuaireEducationChamp do
describe '#update_with_external_data!' do
let(:champ) { create(:champ_annuaire_education, data: "any data") }
subject { champ.update_with_external_data!(data: data) }
shared_examples "a data updater (without updating the value)" do |data|
it { expect { subject }.to change { champ.reload.data }.to(data) }
it { expect { subject }.not_to change { champ.reload.value } }
end
context 'when data is nil' do
let(:data) { nil }
it_behaves_like "a data updater (without updating the value)", nil
end
context 'when data is empty' do
let(:data) { '' }
it_behaves_like "a data updater (without updating the value)", ''
end
context 'when data is inconsistent' do
let(:data) { { 'yo' => 'lo' } }
it_behaves_like "a data updater (without updating the value)", { 'yo' => 'lo' }
end
context 'when data is consistent' do
let(:data) {
{
'nom_etablissement': "karrigel an ankou",
'nom_commune' => 'kumun',
'identifiant_de_l_etablissement' => '666667'
}.with_indifferent_access
}
it { expect { subject }.to change { champ.reload.data }.to(data) }
it { expect { subject }.to change { champ.reload.value }.to('karrigel an ankou, kumun (666667)') }
end
end
end

View file

@ -1,7 +1,7 @@
describe Champs::CarteChamp do
let(:champ) { Champs::CarteChamp.new(geo_areas: geo_areas, type_de_champ: create(:type_de_champ_carte)) }
let(:value) { '' }
let(:coordinates) { [[2.3859214782714844, 48.87442541960633], [2.3850631713867183, 48.87273183590832], [2.3809432983398438, 48.87081237174292], [2.3859214782714844, 48.87442541960633]] }
let(:coordinates) { [[[2.3859214782714844, 48.87442541960633], [2.3850631713867183, 48.87273183590832], [2.3809432983398438, 48.87081237174292], [2.3859214782714844, 48.87442541960633]]] }
let(:geo_json) do
{
"type" => 'Polygon',

View file

@ -6,9 +6,69 @@ describe Champs::DepartementChamp, type: :model do
Rails.cache.clear
end
let(:champ) { described_class.new }
describe 'validations', vcr: { cassette_name: 'api_geo_departements' } do
describe 'external link' do
subject { build(:champ_departements, external_id: external_id) }
context 'when nil' do
let(:external_id) { nil }
it { is_expected.to be_valid }
end
context 'when blank' do
let(:external_id) { '' }
it { is_expected.not_to be_valid }
end
context 'when included in the departement codes' do
let(:external_id) { "01" }
it { is_expected.to be_valid }
end
context 'when not included in the departement codes' do
let(:external_id) { "totoro" }
it { is_expected.not_to be_valid }
end
end
describe 'value' do
subject { create(:champ_departements) }
before { subject.update_columns(value: value) }
context 'when nil' do
let(:value) { nil }
it { is_expected.to be_valid }
end
context 'when blank' do
let(:value) { '' }
it { is_expected.not_to be_valid }
end
context 'when included in the departement names' do
let(:value) { "Ain" }
it { is_expected.to be_valid }
end
context 'when not included in the departement names' do
let(:value) { "totoro" }
it { is_expected.not_to be_valid }
end
end
end
describe 'value', vcr: { cassette_name: 'api_geo_departements' } do
let(:champ) { described_class.new }
it 'with code having 2 chars' do
champ.value = '01'
expect(champ.external_id).to eq('01')

View file

@ -0,0 +1,37 @@
describe Champs::DossierLinkChamp, type: :model do
describe 'prefilling validations' do
describe 'value' do
subject { build(:champ_dossier_link, value: value).valid?(:prefill) }
context 'when nil' do
let(:value) { nil }
it { expect(subject).to eq(true) }
end
context 'when empty' do
let(:value) { '' }
it { expect(subject).to eq(true) }
end
context 'when an integer' do
let(:value) { 42 }
it { expect(subject).to eq(true) }
end
context 'when a string representing an integer' do
let(:value) { "42" }
it { expect(subject).to eq(true) }
end
context 'when it can be casted as integer' do
let(:value) { 'totoro' }
it { expect(subject).to eq(false) }
end
end
end
end

View file

@ -6,9 +6,156 @@ describe Champs::EpciChamp, type: :model do
Rails.cache.clear
end
let(:champ) { described_class.new }
describe 'validations' do
describe 'code_departement', vcr: { cassette_name: 'api_geo_departements' } do
subject { build(:champ_epci, code_departement: code_departement) }
context 'when nil' do
let(:code_departement) { nil }
it { is_expected.to be_valid }
end
context 'when empty' do
let(:code_departement) { '' }
it { is_expected.not_to be_valid }
end
context 'when included in the departement codes' do
let(:code_departement) { "01" }
it { is_expected.to be_valid }
end
context 'when not included in the departement codes' do
let(:code_departement) { "totoro" }
it { is_expected.not_to be_valid }
end
end
describe 'external_id' do
let(:champ) { build(:champ_epci, code_departement: code_departement, external_id: nil) }
subject { champ }
before do
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
champ.save!
champ.update_columns(external_id: external_id)
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
context 'when code_departement is nil' do
let(:code_departement) { nil }
let(:external_id) { nil }
it { is_expected.to be_valid }
end
context 'when code_departement is not nil and valid' do
let(:code_departement) { "01" }
context 'when external_id is nil' do
let(:external_id) { nil }
it { is_expected.to be_valid }
end
context 'when external_id is empty' do
let(:external_id) { '' }
it { is_expected.not_to be_valid }
end
context 'when external_id is included in the epci codes of the departement' do
let(:external_id) { '200042935' }
it { is_expected.to be_valid }
end
context 'when external_id is not included in the epci codes of the departement' do
let(:external_id) { 'totoro' }
it { is_expected.not_to be_valid }
end
end
end
describe 'value' do
let(:champ) { build(:champ_epci, code_departement: code_departement, external_id: nil, value: nil) }
subject { champ }
before do
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
champ.save!
champ.update_columns(external_id: external_id, value: value)
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
context 'when code_departement is nil' do
let(:code_departement) { nil }
let(:external_id) { nil }
let(:value) { nil }
it { is_expected.to be_valid }
end
context 'when external_id is nil' do
let(:code_departement) { '01' }
let(:external_id) { nil }
let(:value) { nil }
it { is_expected.to be_valid }
end
context 'when code_departement and external_id are not nil and valid' do
let(:code_departement) { '01' }
let(:external_id) { '200042935' }
context 'when value is nil' do
let(:value) { nil }
it { is_expected.to be_valid }
end
context 'when value is empty' do
let(:value) { '' }
it { is_expected.not_to be_valid }
end
context 'when value is in departement epci names' do
let(:value) { 'CA Haut - Bugey Agglomération' }
it { is_expected.to be_valid }
end
context 'when value is not in departement epci names' do
let(:value) { 'totoro' }
it { is_expected.not_to be_valid }
end
end
end
end
describe 'value', vcr: { cassette_name: 'api_geo_epcis' } do
let(:champ) { described_class.new }
it 'with departement and code' do
champ.code_departement = '01'
champ.value = '200042935'

View file

@ -0,0 +1,38 @@
describe Champs::MultipleDropDownListChamp do
describe 'validations' do
describe 'inclusion' do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, drop_down_list_value: "val1\r\nval2\r\nval3") }
subject { build(:champ_multiple_drop_down_list, type_de_champ:, value:) }
context 'when the value is nil' do
let(:value) { nil }
it { is_expected.to be_valid }
end
context 'when the value is an empty string' do
let(:value) { '' }
it { is_expected.to be_valid }
end
context 'when the value is an empty array' do
let(:value) { [] }
it { is_expected.to be_valid }
end
context 'when the value is included in the option list' do
let(:value) { ["val3", "val1"] }
it { is_expected.to be_valid }
end
context 'when the value is not included in the option list' do
let(:value) { ["totoro", "val1"] }
it { is_expected.not_to be_valid }
end
end
end
end

View file

@ -6,7 +6,10 @@ RSpec.describe DossierPrefillableConcern do
let(:dossier) { create(:dossier, :brouillon, procedure: procedure) }
let(:types_de_champ_public) { [] }
subject(:fill) { dossier.prefill!(values); dossier.reload }
subject(:fill) do
dossier.prefill!(values)
dossier.reload
end
shared_examples 'a dossier marked as prefilled' do
it 'marks the dossier as prefilled' do

View file

@ -0,0 +1,69 @@
describe DossierSectionsConcern do
describe '#auto_numbering_section_headers_for?' do
let(:public_libelle) { "Infos" }
let(:private_libelle) { "Infos Private" }
let(:types_de_champ_public) { [{ type: :header_section, libelle: public_libelle }, { type: :header_section, libelle: "Details" }] }
let(:types_de_champ_private) { [{ type: :header_section, libelle: private_libelle }, { type: :header_section, libelle: "Details Private" }] }
let(:procedure) { create(:procedure, :for_individual, types_de_champ_public:, types_de_champ_private:) }
let(:dossier) { create(:dossier, procedure: procedure) }
context "with no section having number" do
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(true) }
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(true) }
end
context "with public section having number" do
let(:public_libelle) { "1 - infos" }
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(false) }
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(true) }
end
context "with private section having number" do
let(:private_libelle) { "1 - infos private" }
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_public[1])).to eq(true) }
it { expect(dossier.auto_numbering_section_headers_for?(dossier.champs_private[1])).to eq(false) }
end
end
describe '#index_for_section_header' do
include Logic
let(:number_stable_id) { 99 }
let(:types_de_champ) {
[
{ type: :header_section, libelle: "Infos" }, { type: :integer_number, stable_id: number_stable_id },
{ type: :header_section, libelle: "Details", condition: ds_eq(champ_value(99), constant(5)) }, { type: :header_section, libelle: "Conclusion" }
]
}
let(:procedure) { create(:procedure, :for_individual, types_de_champ_public: types_de_champ) }
let(:dossier) { create(:dossier, procedure: procedure) }
let(:headers) { dossier.champs_public.filter(&:header_section?) }
let(:number_value) { nil }
before do
dossier.champs_public.find { _1.stable_id == number_stable_id }.update(value: number_value)
dossier.reload
end
context "when there are invisible sections" do
it "index accordingly header sections" do
expect(dossier.index_for_section_header(headers[0])).to eq(1)
expect(headers[1]).not_to be_visible
expect(dossier.index_for_section_header(headers[2])).to eq(2)
end
end
context "when all headers are visible" do
let(:number_value) { 5 }
it "index accordingly header sections" do
expect(dossier.index_for_section_header(headers[0])).to eq(1)
expect(headers[1]).to be_visible
expect(dossier.index_for_section_header(headers[1])).to eq(2)
expect(dossier.index_for_section_header(headers[2])).to eq(3)
end
end
end
end

View file

@ -7,15 +7,17 @@ describe ProcedureStatsConcern do
before do
create_list(:dossier, 2, :brouillon, procedure: procedure)
create(:dossier, :en_instruction, procedure: procedure)
create(:dossier, procedure: procedure, for_procedure_preview: true)
create(:dossier, :accepte, procedure: procedure, hidden_by_administration_at: Time.zone.now)
end
it "returns the funnel stats" do
expect(stats_dossiers_funnel).to match(
[
['Démarrés', procedure.dossiers.count],
['Déposés', procedure.dossiers.state_not_brouillon.count],
['Instruction débutée', procedure.dossiers.state_instruction_commencee.count],
['Traités', procedure.dossiers.state_termine.count]
['Démarrés', procedure.dossiers.visible_by_user_or_administration.count],
['Déposés', procedure.dossiers.visible_by_administration.count],
['Instruction débutée', procedure.dossiers.visible_by_administration.state_instruction_commencee.count],
['Traités', procedure.dossiers.visible_by_administration.state_termine.count]
]
)
end

View file

@ -0,0 +1,95 @@
RSpec.describe RNAChampAssociationFetchableConcern do
describe '.fetch_association!' do
let!(:champ) { create(:champ_rna, data: "not nil data", value: 'W173847273') }
before do
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\//)
.to_return(body: body, status: status)
allow_any_instance_of(APIEntrepriseToken).to receive(:expired?).and_return(false)
end
subject(:fetch_association!) { champ.fetch_association!(rna) }
shared_examples "an association fetcher" do |expected_result, expected_error, expected_value, expected_data|
it { expect { fetch_association! }.to change { champ.reload.value }.to(expected_value) }
it { expect { fetch_association! }.to change { champ.reload.data }.to(expected_data) }
it { expect(fetch_association!).to eq(expected_result) }
it 'populates the association_fetch_error_key when an error occurs' do
fetch_association!
expect(champ.association_fetch_error_key).to eq(expected_error)
end
end
context 'when the RNA is empty' do
let(:rna) { '' }
let(:status) { 422 }
let(:body) { '' }
it_behaves_like "an association fetcher", false, :empty, '', nil
end
context 'when the RNA is invalid' do
let(:rna) { '1234' }
let(:status) { 422 }
let(:body) { '' }
it_behaves_like "an association fetcher", false, :invalid, '1234', nil
end
context 'when the RNA is unknow' do
let(:rna) { 'W111111111' }
let(:status) { 404 }
let(:body) { '' }
it_behaves_like "an association fetcher", false, :not_found, 'W111111111', nil
end
context 'when the API is unavailable due to network error' do
let(:rna) { 'W595001988' }
let(:status) { 503 }
let(:body) { File.read('spec/fixtures/files/api_entreprise/associations.json') }
before { expect(APIEntrepriseService).to receive(:api_up?).and_return(false) }
it_behaves_like "an association fetcher", false, :network_error, 'W595001988', nil
end
context 'when the RNA informations are retrieved successfully' do
let(:rna) { 'W595001988' }
let(:status) { 200 }
let(:body) { File.read('spec/fixtures/files/api_entreprise/associations.json') }
it_behaves_like "an association fetcher", true, nil, 'W595001988', {
"association_id" => "W595001988",
"association_titre" => "UN SUR QUATRE",
"association_objet" => "valoriser, transmettre et partager auprès des publics les plus larges possibles, les bienfaits de l'immigration, la richesse de la diversité et la curiosité de l'autre autrement",
"association_siret" => nil,
"association_date_creation" => "2014-01-23",
"association_date_declaration" => "2014-01-24",
"association_date_publication" => "2014-02-08",
"association_date_dissolution" => "0001-01-01",
"association_adresse_siege" => {
"complement" => "",
"numero_voie" => "61",
"type_voie" => "RUE",
"libelle_voie" => "des Noyers",
"distribution" => "_",
"code_insee" => "93063",
"code_postal" => "93230",
"commune" => "Romainville"
},
"association_code_civilite_dirigeant" => "PM",
"association_civilite_dirigeant" => "Monsieur le Président",
"association_code_etat" => "A",
"association_etat" => "Active",
"association_code_groupement" => "S",
"association_groupement" => "simple",
"association_mise_a_jour" => 1392295833,
"association_rna" => "W595001988"
}
end
end
end

View file

@ -0,0 +1,115 @@
RSpec.describe SiretChampEtablissementFetchableConcern do
describe '.fetch_etablissement!' do
let(:api_etablissement_status) { 200 }
let(:api_etablissement_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') }
let(:token_expired) { false }
let!(:champ) { create(:champ_siret) }
before do
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/)
.to_return(status: api_etablissement_status, body: api_etablissement_body)
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siret[0..8]}/)
.to_return(body: File.read('spec/fixtures/files/api_entreprise/entreprises.json'), status: 200)
allow_any_instance_of(APIEntrepriseToken).to receive(:roles)
.and_return(["attestations_fiscales", "attestations_sociales", "bilans_entreprise_bdf"])
allow_any_instance_of(APIEntrepriseToken).to receive(:expired?).and_return(token_expired)
end
subject(:fetch_etablissement!) { champ.fetch_etablissement!(siret, build_stubbed(:user)) }
shared_examples 'an error occured' do |error|
it { expect { fetch_etablissement! }.to change { champ.reload.etablissement }.to(nil) }
it { expect { fetch_etablissement! }.to change { Etablissement.count }.by(-1) }
it { expect(fetch_etablissement!).to eq(false) }
it 'populates the etablissement_fetch_error_key' do
fetch_etablissement!
expect(champ.etablissement_fetch_error_key).to eq(error)
end
end
context 'when the SIRET is empty' do
let(:siret) { '' }
it_behaves_like 'an error occured', :empty
end
context "when the SIRET is invalid because of it's length" do
let(:siret) { '1234' }
it_behaves_like 'an error occured', :invalid_length
end
context "when the SIRET is invalid because of it's checksum" do
let(:siret) { '82812345600023' }
it_behaves_like 'an error occured', :invalid_checksum
end
context 'when the API is unavailable due to network error' do
let(:siret) { '82161143100015' }
let(:api_etablissement_status) { 503 }
before { expect(APIEntrepriseService).to receive(:api_up?).and_return(true) }
it_behaves_like 'an error occured', :network_error
it 'sends the error to Sentry' do
expect(Sentry).to receive(:capture_exception)
fetch_etablissement!
end
end
context 'when the API is unavailable due to an api maintenance or pb' do
let(:siret) { '82161143100015' }
let(:api_etablissement_status) { 502 }
before { expect(APIEntrepriseService).to receive(:api_up?).and_return(false) }
it { expect { fetch_etablissement! }.to change { champ.reload.value }.to(siret) }
it { expect { fetch_etablissement! }.to change { champ.reload.etablissement } }
it { expect { fetch_etablissement! }.to change { champ.reload.etablissement.as_degraded_mode? }.to(true) }
it { expect { fetch_etablissement! }.to change { Etablissement.count }.by(1) }
it { expect(fetch_etablissement!).to eq(false) }
it 'populates the etablissement_fetch_error_key' do
fetch_etablissement!
expect(champ.etablissement_fetch_error_key).to eq(:api_entreprise_down)
end
end
context 'when the SIRET is valid but unknown' do
let(:siret) { '00000000000000' }
let(:api_etablissement_status) { 404 }
it_behaves_like 'an error occured', :not_found
end
context 'when the SIRET informations are retrieved successfully' do
let(:siret) { '41816609600051' }
let(:api_etablissement_status) { 200 }
let(:api_etablissement_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') }
it { expect { fetch_etablissement! }.to change { champ.reload.value }.to(siret) }
it { expect { fetch_etablissement! }.to change { champ.reload.etablissement.siret }.to(siret) }
it { expect { fetch_etablissement! }.to change { champ.reload.etablissement.naf }.to("6202A") }
it { expect { fetch_etablissement! }.to change { Etablissement.count }.by(1) }
it { expect(fetch_etablissement!).to eq(true) }
it "fetches the entreprise raison sociale" do
fetch_etablissement!
expect(champ.reload.etablissement.entreprise_raison_sociale).to eq("OCTO-TECHNOLOGY")
end
end
end
end

View file

@ -1516,8 +1516,8 @@ describe Dossier do
{
type: 'Feature',
geometry: {
'coordinates' => [[[2.428439855575562, 46.538476837725796], [2.4284291267395024, 46.53842148758162], [2.4282521009445195, 46.53841410755813], [2.42824137210846, 46.53847314771794], [2.428284287452698, 46.53847314771794], [2.428364753723145, 46.538487907747864], [2.4284291267395024, 46.538491597754714], [2.428439855575562, 46.538476837725796]]],
'type' => 'Polygon'
coordinates: [[[2.428439855575562, 46.538476837725796], [2.4284291267395024, 46.53842148758162], [2.4282521009445195, 46.53841410755813], [2.42824137210846, 46.53847314771794], [2.428284287452698, 46.53847314771794], [2.428364753723145, 46.538487907747864], [2.4284291267395024, 46.538491597754714], [2.428439855575562, 46.538476837725796]]],
type: 'Polygon'
},
properties: {
area: 103.6,
@ -1861,7 +1861,7 @@ describe Dossier do
let(:champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }
before { dossier.champs_public << champ_repetition }
it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(2) }
it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.count).to eq(4) }
it { expect(Champs::RepetitionChamp.where(dossier: new_dossier).first.champs.ids).not_to eq(champ_repetition.champs.ids) }
end
@ -2080,6 +2080,15 @@ describe Dossier do
end
end
describe 'update procedure dossiers count' do
let(:dossier) { create(:dossier, :brouillon, :with_individual) }
it 'update procedure dossiers count when passing to construction' do
expect(dossier.procedure).to receive(:compute_dossiers_count)
dossier.passer_en_construction!
end
end
private
def count_for_month(processed_by_month, month)

View file

@ -23,7 +23,7 @@ RSpec.describe GeoArea, type: :model do
it { expect(geo_area.location).to eq("46°32'19\"N 2°25'42\"E") }
end
describe '#rgeo_geometry' do
describe '#geometry' do
let(:geo_area) { build(:geo_area, :polygon, champ: nil) }
let(:polygon) do
{
@ -47,9 +47,9 @@ RSpec.describe GeoArea, type: :model do
context 'polygon_with_extra_coordinate' do
let(:geo_area) { build(:geo_area, :polygon_with_extra_coordinate, champ: nil) }
before { geo_area.valid? }
it { expect(geo_area.geometry).not_to eq(polygon) }
it { expect(geo_area.safe_geometry).to eq(polygon) }
it { expect(geo_area.geometry).to eq(polygon) }
end
end

View file

@ -22,7 +22,7 @@ RSpec.describe PrefillDescription, type: :model do
it { expect(types_de_champ.count).to eq(1) }
it { expect(types_de_champ.first).to eql(TypesDeChamp::PrefillTypeDeChamp.build(type_de_champ)) }
it { expect(types_de_champ.first).to eql(TypesDeChamp::PrefillTypeDeChamp.build(type_de_champ, procedure.active_revision)) }
shared_examples "filters out non fillable types de champ" do |type_de_champ_name|
context "when the procedure has a #{type_de_champ_name} champ" do
@ -86,36 +86,80 @@ RSpec.describe PrefillDescription, type: :model do
end
end
describe '#prefill_link' do
describe '#prefill_link', vcr: { cassette_name: 'api_geo_regions' } do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { create(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_text) { build(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_epci) { build(:type_de_champ_epci, procedure: procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, :with_region_types_de_champ, procedure: procedure) }
let(:prefillable_subchamps) { TypesDeChamp::PrefillRepetitionTypeDeChamp.new(type_de_champ_repetition, procedure.active_revision).send(:prefillable_subchamps) }
let(:region_repetition) { prefillable_subchamps.third }
let(:prefill_description) { described_class.new(procedure) }
before { prefill_description.update(selected_type_de_champ_ids: [type_de_champ.id]) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
prefill_description.update(selected_type_de_champ_ids: [type_de_champ_text.id, type_de_champ_epci.id, type_de_champ_repetition.id])
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
it "builds the URL to create a new prefilled dossier" do
expect(prefill_description.prefill_link).to eq(
commencer_url(
path: procedure.path,
"champ_#{type_de_champ.to_typed_id}" => I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")
CGI.unescape(
commencer_url(
path: procedure.path,
"champ_#{type_de_champ_text.to_typed_id_for_query}" => TypesDeChamp::PrefillTypeDeChamp.build(type_de_champ_text, procedure.active_revision).example_value,
"champ_#{type_de_champ_epci.to_typed_id_for_query}" => TypesDeChamp::PrefillTypeDeChamp.build(type_de_champ_epci, procedure.active_revision).example_value,
"champ_#{type_de_champ_repetition.to_typed_id_for_query}" => TypesDeChamp::PrefillTypeDeChamp.build(type_de_champ_repetition, procedure.active_revision).example_value
)
)
)
end
end
describe '#prefill_query' do
describe '#prefill_query', vcr: { cassette_name: 'api_geo_regions' } do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { create(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_text) { create(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_epci) { TypesDeChamp::PrefillTypeDeChamp.build(create(:type_de_champ_epci, procedure: procedure), procedure.active_revision) }
let(:type_de_champ_repetition) { build(:type_de_champ_repetition, :with_types_de_champ, :with_region_types_de_champ, procedure: procedure) }
let(:prefillable_subchamps) { TypesDeChamp::PrefillRepetitionTypeDeChamp.new(type_de_champ_repetition, procedure.active_revision).send(:prefillable_subchamps) }
let(:text_repetition) { prefillable_subchamps.first }
let(:integer_repetition) { prefillable_subchamps.second }
let(:region_repetition) { prefillable_subchamps.third }
let(:prefill_description) { described_class.new(procedure) }
let(:expected_query) do
<<~TEXT
curl --request POST '#{api_public_v1_dossiers_url(procedure)}' \\
--header 'Content-Type: application/json' \\
--data '{"champ_#{type_de_champ.to_typed_id}": "#{I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")}"}'
--data '{"champ_#{type_de_champ_text.to_typed_id_for_query}":"Texte court","champ_#{type_de_champ_epci.to_typed_id_for_query}":["01","200042935"],"champ_#{type_de_champ_repetition.to_typed_id_for_query}":[{"champ_#{text_repetition.to_typed_id_for_query}":"Texte court","champ_#{integer_repetition.to_typed_id_for_query}":"42","champ_#{region_repetition.to_typed_id_for_query}":"53"},{"champ_#{text_repetition.to_typed_id_for_query}":"Texte court","champ_#{integer_repetition.to_typed_id_for_query}":"42","champ_#{region_repetition.to_typed_id_for_query}":"53"}]}'
TEXT
end
before { prefill_description.update(selected_type_de_champ_ids: [type_de_champ.id]) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
prefill_description.update(selected_type_de_champ_ids: [type_de_champ_text.id, type_de_champ_epci.id, type_de_champ_repetition.id])
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
it "builds the query to create a new prefilled dossier" do
expect(prefill_description.prefill_query).to eq(expected_query)

View file

@ -1,5 +1,5 @@
RSpec.describe PrefillParams do
describe "#to_a", vcr: { cassette_name: 'api_geo_regions' } do
describe "#to_a", vcr: { cassette_name: 'api_geo_all' } do
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:, types_de_champ_private:) }
@ -12,6 +12,18 @@ RSpec.describe PrefillParams do
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
VCR.insert_cassette('api_geo_regions')
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_communes')
VCR.insert_cassette('api_geo_epcis')
end
after do
VCR.eject_cassette('api_geo_regions')
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_communes')
VCR.eject_cassette('api_geo_epcis')
end
context "when the stable ids match the TypeDeChamp of the corresponding procedure" do
@ -26,8 +38,8 @@ RSpec.describe PrefillParams do
let(:params) {
{
"champ_#{type_de_champ_1.to_typed_id}" => value_1,
"champ_#{type_de_champ_2.to_typed_id}" => value_2
"champ_#{type_de_champ_1.to_typed_id_for_query}" => value_1,
"champ_#{type_de_champ_2.to_typed_id_for_query}" => value_2
}
}
@ -43,7 +55,7 @@ RSpec.describe PrefillParams do
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:types_de_champ_public) { [{ type: :text }] }
let(:params) { { type_de_champ.to_typed_id => "value" } }
let(:params) { { type_de_champ.to_typed_id_for_query => "value" } }
it "filters out the champ" do
expect(prefill_params_array).to match([])
@ -61,7 +73,7 @@ RSpec.describe PrefillParams do
context 'when there is no Champ that matches the TypeDeChamp with the given stable id' do
let!(:type_de_champ) { create(:type_de_champ_text) } # goes to another procedure
let(:params) { { "champ_#{type_de_champ.to_typed_id}" => "value" } }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => "value" } }
it "filters out the param" do
expect(prefill_params_array).to match([])
@ -72,12 +84,12 @@ RSpec.describe PrefillParams do
context "when the type de champ is authorized (#{type_de_champ_type})" do
let(:types_de_champ_public) { [{ type: type_de_champ_type }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:champ_id) { find_champ_by_stable_id(dossier, type_de_champ.stable_id).id }
let(:champ) { find_champ_by_stable_id(dossier, type_de_champ.stable_id) }
let(:params) { { "champ_#{type_de_champ.to_typed_id}" => value } }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => value } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([{ id: champ_id, value: value }])
it "builds an array of hash matching the given params" do
expect(prefill_params_array).to match([{ id: champ.id }.merge(attributes(champ, value))])
end
end
end
@ -86,12 +98,12 @@ RSpec.describe PrefillParams do
context "when the type de champ is authorized (#{type_de_champ_type})" do
let(:types_de_champ_private) { [{ type: type_de_champ_type }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_private.first }
let(:champ_id) { find_champ_by_stable_id(dossier, type_de_champ.stable_id).id }
let(:champ) { find_champ_by_stable_id(dossier, type_de_champ.stable_id) }
let(:params) { { "champ_#{type_de_champ.to_typed_id}" => value } }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => value } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([{ id: champ_id, value: value }])
it "builds an array of hash matching the given params" do
expect(prefill_params_array).to match([{ id: champ.id }.merge(attributes(champ, value))])
end
end
end
@ -100,7 +112,7 @@ RSpec.describe PrefillParams do
let(:types_de_champ_public) { [{ type: type_de_champ_type }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:params) { { "champ_#{type_de_champ.to_typed_id}" => value } }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => value } }
context "when the type de champ is unauthorized (#{type_de_champ_type})" do
it "filters out the param" do
@ -118,6 +130,7 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ public value that is authorized", :iban, "value"
it_behaves_like "a champ public value that is authorized", :civilite, "M."
it_behaves_like "a champ public value that is authorized", :pays, "FR"
it_behaves_like "a champ public value that is authorized", :regions, "03"
it_behaves_like "a champ public value that is authorized", :date, "2022-12-22"
it_behaves_like "a champ public value that is authorized", :datetime, "2022-12-22T10:30"
it_behaves_like "a champ public value that is authorized", :yes_no, "true"
@ -125,7 +138,28 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ public value that is authorized", :checkbox, "true"
it_behaves_like "a champ public value that is authorized", :checkbox, "false"
it_behaves_like "a champ public value that is authorized", :drop_down_list, "value"
it_behaves_like "a champ public value that is authorized", :regions, "03"
it_behaves_like "a champ public value that is authorized", :departements, "03"
it_behaves_like "a champ public value that is authorized", :communes, ['01', '01457']
it_behaves_like "a champ public value that is authorized", :annuaire_education, "0050009H"
it_behaves_like "a champ public value that is authorized", :multiple_drop_down_list, ["val1", "val2"]
it_behaves_like "a champ public value that is authorized", :dossier_link, "1"
it_behaves_like "a champ public value that is authorized", :epci, ['01', '200042935']
it_behaves_like "a champ public value that is authorized", :siret, "13002526500013"
it_behaves_like "a champ public value that is authorized", :rna, "value"
context "when the public type de champ is authorized (repetition)" do
let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :text }] }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:type_de_champ_child_value) { "value" }
let(:type_de_champ_child_value2) { "value2" }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => [{ "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value }, { "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value2 }] } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([{ id: type_de_champ_child.champ.first.id, value: type_de_champ_child_value }, { id: type_de_champ_child.champ.second.id, value: type_de_champ_child_value2 }])
end
end
it_behaves_like "a champ private value that is authorized", :text, "value"
it_behaves_like "a champ private value that is authorized", :textarea, "value"
@ -136,6 +170,7 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ private value that is authorized", :iban, "value"
it_behaves_like "a champ private value that is authorized", :civilite, "M."
it_behaves_like "a champ private value that is authorized", :pays, "FR"
it_behaves_like "a champ private value that is authorized", :regions, "93"
it_behaves_like "a champ private value that is authorized", :date, "2022-12-22"
it_behaves_like "a champ private value that is authorized", :datetime, "2022-12-22T10:30"
it_behaves_like "a champ private value that is authorized", :yes_no, "true"
@ -144,23 +179,42 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ private value that is authorized", :checkbox, "false"
it_behaves_like "a champ private value that is authorized", :drop_down_list, "value"
it_behaves_like "a champ private value that is authorized", :regions, "93"
it_behaves_like "a champ private value that is authorized", :rna, "value"
it_behaves_like "a champ private value that is authorized", :siret, "13002526500013"
it_behaves_like "a champ private value that is authorized", :departements, "03"
it_behaves_like "a champ private value that is authorized", :communes, ['01', '01457']
it_behaves_like "a champ private value that is authorized", :annuaire_education, "0050009H"
it_behaves_like "a champ private value that is authorized", :multiple_drop_down_list, ["val1", "val2"]
it_behaves_like "a champ private value that is authorized", :dossier_link, "1"
it_behaves_like "a champ private value that is authorized", :epci, ['01', '200042935']
context "when the private type de champ is authorized (repetition)" do
let(:types_de_champ_private) { [{ type: :repetition, children: [{ type: :text }] }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_private.first }
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:type_de_champ_child_value) { "value" }
let(:type_de_champ_child_value2) { "value2" }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => [{ "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value }, { "champ_#{type_de_champ_child.to_typed_id_for_query}" => type_de_champ_child_value2 }] } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([{ id: type_de_champ_child.champ.first.id, value: type_de_champ_child_value }, { id: type_de_champ_child.champ.second.id, value: type_de_champ_child_value2 }])
end
end
it_behaves_like "a champ public value that is unauthorized", :decimal_number, "non decimal string"
it_behaves_like "a champ public value that is unauthorized", :integer_number, "non integer string"
it_behaves_like "a champ public value that is unauthorized", :number, "value"
it_behaves_like "a champ public value that is unauthorized", :communes, "value"
it_behaves_like "a champ public value that is unauthorized", :dossier_link, "value"
it_behaves_like "a champ public value that is unauthorized", :titre_identite, "value"
it_behaves_like "a champ public value that is unauthorized", :civilite, "value"
it_behaves_like "a champ public value that is unauthorized", :date, "value"
it_behaves_like "a champ public value that is unauthorized", :datetime, "value"
it_behaves_like "a champ public value that is unauthorized", :datetime, "12-22-2022T10:30"
it_behaves_like "a champ public value that is unauthorized", :multiple_drop_down_list, "value"
it_behaves_like "a champ public value that is unauthorized", :linked_drop_down_list, "value"
it_behaves_like "a champ public value that is unauthorized", :header_section, "value"
it_behaves_like "a champ public value that is unauthorized", :explication, "value"
it_behaves_like "a champ public value that is unauthorized", :piece_justificative, "value"
it_behaves_like "a champ public value that is unauthorized", :repetition, "value"
it_behaves_like "a champ public value that is unauthorized", :cnaf, "value"
it_behaves_like "a champ public value that is unauthorized", :dgfip, "value"
it_behaves_like "a champ public value that is unauthorized", :pole_emploi, "value"
@ -170,9 +224,32 @@ RSpec.describe PrefillParams do
it_behaves_like "a champ public value that is unauthorized", :pays, "value"
it_behaves_like "a champ public value that is unauthorized", :regions, "value"
it_behaves_like "a champ public value that is unauthorized", :departements, "value"
it_behaves_like "a champ public value that is unauthorized", :siret, "value"
it_behaves_like "a champ public value that is unauthorized", :rna, "value"
it_behaves_like "a champ public value that is unauthorized", :annuaire_education, "value"
it_behaves_like "a champ public value that is unauthorized", :communes, "value"
it_behaves_like "a champ public value that is unauthorized", :multiple_drop_down_list, ["value"]
context "when the public type de champ is unauthorized because of wrong value format (repetition)" do
let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :text }] }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => "value" } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([])
end
end
context "when the public type de champ is unauthorized because of wrong value typed_id (repetition)" do
let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :text }] }] }
let(:type_de_champ) { procedure.published_revision.types_de_champ_public.first }
let(:type_de_champ_child) { procedure.published_revision.children_of(type_de_champ).first }
let(:params) { { "champ_#{type_de_champ.to_typed_id_for_query}" => ["{\"wrong\":\"value\"}", "{\"wrong\":\"value2\"}"] } }
it "builds an array of hash(id, value) matching the given params" do
expect(prefill_params_array).to match([])
end
end
end
private
@ -180,4 +257,10 @@ RSpec.describe PrefillParams do
def find_champ_by_stable_id(dossier, stable_id)
dossier.champs.joins(:type_de_champ).find_by(types_de_champ: { stable_id: stable_id })
end
def attributes(champ, value)
TypesDeChamp::PrefillTypeDeChamp
.build(champ.type_de_champ, procedure.active_revision)
.to_assignable_attributes(champ, value)
end
end

View file

@ -134,15 +134,15 @@ describe ProcedureRevision do
end
it 'move down' do
expect(draft.children_of(type_de_champ_repetition).index(second_child)).to eq(1)
draft.move_type_de_champ(second_child.stable_id, 2)
expect(draft.children_of(type_de_champ_repetition).index(second_child)).to eq(2)
draft.move_type_de_champ(second_child.stable_id, 3)
expect(draft.children_of(type_de_champ_repetition).index(second_child)).to eq(3)
end
it 'move up' do
expect(draft.children_of(type_de_champ_repetition).index(last_child)).to eq(2)
expect(draft.children_of(type_de_champ_repetition).index(last_child)).to eq(3)
draft.move_type_de_champ(last_child.stable_id, 0)
@ -205,13 +205,13 @@ describe ProcedureRevision do
it 'reorders' do
children = draft.children_of(type_de_champ_repetition)
expect(children.pluck(:position)).to eq([0, 1, 2])
expect(children.pluck(:position)).to eq([0, 1, 2, 3])
draft.remove_type_de_champ(children[1].stable_id)
children.reload
expect(children.pluck(:position)).to eq([0, 1])
expect(children.pluck(:position)).to eq([0, 1, 2])
end
end
end
@ -242,8 +242,8 @@ describe ProcedureRevision do
new_draft.remove_type_de_champ(child.stable_id)
expect { child.reload }.not_to raise_error
expect(draft.children_of(type_de_champ_repetition).size).to eq(1)
expect(new_draft.children_of(type_de_champ_repetition)).to be_empty
expect(draft.children_of(type_de_champ_repetition).size).to eq(2)
expect(new_draft.children_of(type_de_champ_repetition).size).to eq(1)
end
it 'can remove the parent only in the new revision' do
@ -291,7 +291,7 @@ describe ProcedureRevision do
let(:procedure) { create(:procedure, :with_repetition) }
it 'should have the same tdcs with different links' do
expect(new_draft.types_de_champ.count).to eq(2)
expect(new_draft.types_de_champ.count).to eq(3)
expect(new_draft.types_de_champ).to eq(draft.types_de_champ)
new_repetition, new_child = new_draft.types_de_champ.partition(&:repetition?).map(&:first)
@ -320,7 +320,7 @@ describe ProcedureRevision do
it do
expect(procedure.revisions.size).to eq(2)
expect(draft.revision_types_de_champ.where.not(parent_id: nil).size).to eq(1)
expect(draft.revision_types_de_champ.where.not(parent_id: nil).size).to eq(2)
end
end
end
@ -639,9 +639,10 @@ describe ProcedureRevision do
context 'with a repetition tdc' do
let(:procedure) { create(:procedure, :with_repetition) }
let!(:parent) { draft.types_de_champ.find(&:repetition?) }
let!(:child) { draft.types_de_champ.reject(&:repetition?).first }
let!(:first_child) { draft.types_de_champ.reject(&:repetition?).first }
let!(:second_child) { draft.types_de_champ.reject(&:repetition?).second }
it { expect(draft.children_of(parent)).to match([child]) }
it { expect(draft.children_of(parent)).to match([first_child, second_child]) }
context 'with multiple child' do
let(:child_position_2) { create(:type_de_champ_text) }
@ -654,7 +655,7 @@ describe ProcedureRevision do
end
it 'returns the children in order' do
expect(draft.children_of(parent)).to eq([child, child_position_1, child_position_2])
expect(draft.children_of(parent)).to eq([first_child, second_child, child_position_1, child_position_2])
end
end
@ -668,13 +669,13 @@ describe ProcedureRevision do
before do
new_draft
.revision_types_de_champ
.where(type_de_champ: child)
.where(type_de_champ: first_child)
.update(type_de_champ: new_child)
end
it 'returns the children regarding the revision' do
expect(draft.children_of(parent)).to match([child])
expect(new_draft.children_of(parent)).to match([new_child])
expect(draft.children_of(parent)).to match([first_child, second_child])
expect(new_draft.children_of(parent)).to match([new_child, second_child])
end
end
end

View file

@ -9,6 +9,20 @@ describe Procedure do
it { expect(subject.without_continuation_mail_template).to be_a(Mails::WithoutContinuationMail) }
end
describe 'compute_dossiers_count' do
let(:procedure) { create(:procedure_with_dossiers, dossiers_count: 2, dossiers_count_computed_at: Time.zone.now - Procedure::DOSSIERS_COUNT_EXPIRING) }
it 'caches estimated_dossiers_count' do
procedure.dossiers.each(&:passer_en_construction!)
expect { procedure.compute_dossiers_count }.to change(procedure, :estimated_dossiers_count).from(nil).to(2)
expect { create(:dossier, procedure: procedure).passer_en_construction! }.not_to change(procedure, :estimated_dossiers_count)
Timecop.freeze(Time.zone.now + Procedure::DOSSIERS_COUNT_EXPIRING)
expect { procedure.compute_dossiers_count }.to change(procedure, :estimated_dossiers_count).from(2).to(3)
Timecop.return
end
end
describe 'initiated_mail' do
let(:procedure) { create(:procedure) }

View file

@ -4,10 +4,9 @@ describe Stat do
describe '.deleted_dossiers_states' do
subject { Stat.send(:deleted_dossiers_states) }
it 'find counts for columns' do
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :termine)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :accepte)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :en_construction, deleted_at: 1.month.ago)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :en_construction, deleted_at: 2.months.ago)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :brouillon, deleted_at: 3.months.ago)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :en_construction, deleted_at: 3.months.ago)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :en_instruction, deleted_at: 3.months.ago)
create(:deleted_dossier, dossier_id: create(:dossier).id, state: :accepte, deleted_at: 3.months.ago)
@ -17,10 +16,10 @@ describe Stat do
expect(subject["not_brouillon"]).to eq(8)
expect(subject["dossiers_depose_avant_30_jours"]).to eq(1)
expect(subject["dossiers_deposes_entre_60_et_30_jours"]).to eq(1)
expect(subject["brouillon"]).to eq(1)
expect(subject["brouillon"]).to eq(0)
expect(subject["en_construction"]).to eq(3)
expect(subject["en_instruction"]).to eq(1)
expect(subject["termines"]).to eq(3)
expect(subject["termines"]).to eq(4)
end
end

View file

@ -95,7 +95,7 @@ describe TypeDeChamp do
let(:target_type_champ) { TypeDeChamp.type_champs.fetch(:text) }
it 'removes the children types de champ' do
expect(procedure.draft_revision.children_of(tdc)).to be_empty
expect(procedure.draft_revision.reload.children_of(tdc)).to be_empty
end
end
end
@ -246,30 +246,36 @@ describe TypeDeChamp do
it_behaves_like "a prefillable type de champ", :type_de_champ_datetime
it_behaves_like "a prefillable type de champ", :type_de_champ_civilite
it_behaves_like "a prefillable type de champ", :type_de_champ_pays
it_behaves_like "a prefillable type de champ", :type_de_champ_regions
it_behaves_like "a prefillable type de champ", :type_de_champ_departements
it_behaves_like "a prefillable type de champ", :type_de_champ_communes
it_behaves_like "a prefillable type de champ", :type_de_champ_yes_no
it_behaves_like "a prefillable type de champ", :type_de_champ_checkbox
it_behaves_like "a prefillable type de champ", :type_de_champ_drop_down_list
it_behaves_like "a prefillable type de champ", :type_de_champ_regions
it_behaves_like "a prefillable type de champ", :type_de_champ_repetition
it_behaves_like "a prefillable type de champ", :type_de_champ_annuaire_education
it_behaves_like "a prefillable type de champ", :type_de_champ_multiple_drop_down_list
it_behaves_like "a prefillable type de champ", :type_de_champ_epci
it_behaves_like "a prefillable type de champ", :type_de_champ_dossier_link
it_behaves_like "a prefillable type de champ", :type_de_champ_siret
it_behaves_like "a prefillable type de champ", :type_de_champ_rna
it_behaves_like "a non-prefillable type de champ", :type_de_champ_number
it_behaves_like "a non-prefillable type de champ", :type_de_champ_communes
it_behaves_like "a non-prefillable type de champ", :type_de_champ_dossier_link
it_behaves_like "a non-prefillable type de champ", :type_de_champ_titre_identite
it_behaves_like "a non-prefillable type de champ", :type_de_champ_multiple_drop_down_list
it_behaves_like "a non-prefillable type de champ", :type_de_champ_linked_drop_down_list
it_behaves_like "a non-prefillable type de champ", :type_de_champ_header_section
it_behaves_like "a non-prefillable type de champ", :type_de_champ_explication
it_behaves_like "a non-prefillable type de champ", :type_de_champ_piece_justificative
it_behaves_like "a non-prefillable type de champ", :type_de_champ_repetition
it_behaves_like "a non-prefillable type de champ", :type_de_champ_cnaf
it_behaves_like "a non-prefillable type de champ", :type_de_champ_dgfip
it_behaves_like "a non-prefillable type de champ", :type_de_champ_pole_emploi
it_behaves_like "a non-prefillable type de champ", :type_de_champ_mesri
it_behaves_like "a non-prefillable type de champ", :type_de_champ_carte
it_behaves_like "a non-prefillable type de champ", :type_de_champ_address
it_behaves_like "a non-prefillable type de champ", :type_de_champ_departements
it_behaves_like "a non-prefillable type de champ", :type_de_champ_siret
it_behaves_like "a non-prefillable type de champ", :type_de_champ_rna
it_behaves_like "a non-prefillable type de champ", :type_de_champ_annuaire_education
end
describe '#normalize_libelle' do
it { expect(create(:type_de_champ, :header_section, libelle: " 2.3 Test").libelle).to eq("2.3 Test") }
it { expect(create(:type_de_champ, libelle: " fix me ").libelle).to eq("fix me") }
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_annuaire_education, procedure: procedure) }
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#to_assignable_attributes' do
let(:champ) { create(:champ_annuaire_education, type_de_champ: type_de_champ) }
subject { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when the value is nil' do
let(:value) { nil }
it { is_expected.to eq(nil) }
end
context 'when the value is empty' do
let(:value) { '' }
it { is_expected.to eq(nil) }
end
context 'when the value is present' do
let(:value) { '0050009H' }
it { is_expected.to match({ id: champ.id, external_id: '0050009H', value: '0050009H' }) }
end
end
end

View file

@ -0,0 +1,123 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillCommuneTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_communes, procedure: procedure) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
before do
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_communes')
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_communes')
end
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#all_possible_values' do
let(:expected_values) do
departements.map { |departement| "#{departement[:code]} (#{departement[:name]}) : https://geo.api.gouv.fr/communes?codeDepartement=#{departement[:code]}" }
end
subject(:all_possible_values) { described_class.new(type_de_champ, procedure.active_revision).all_possible_values }
it { expect(all_possible_values).to match(expected_values) }
end
describe '#example_value' do
let(:departement_code) { departements.pick(:code) }
let(:commune_code) { APIGeoService.communes(departement_code).pick(:code) }
subject(:example_value) { described_class.new(type_de_champ, procedure.active_revision).example_value }
it { is_expected.to eq([departement_code, commune_code]) }
end
describe '#to_assignable_attributes' do
let(:champ) { create(:champ_communes, type_de_champ: type_de_champ) }
subject(:to_assignable_attributes) do
described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value)
end
context 'when the value is nil' do
let(:value) { nil }
it { is_expected.to match(nil) }
end
context 'when the value is empty' do
let(:value) { '' }
it { is_expected.to match(nil) }
end
context 'when the value is a string' do
let(:value) { 'hello' }
it { is_expected.to match(nil) }
end
context 'when the value is an array of one element' do
context 'when the first element is a valid departement code' do
let(:value) { ['01'] }
it { is_expected.to match({ id: champ.id, code_departement: '01', departement: 'Ain' }) }
end
context 'when the first element is not a valid departement code' do
let(:value) { ['totoro'] }
it { is_expected.to match(nil) }
end
end
context 'when the value is an array of two elements' do
context 'when the first element is a valid departement code' do
context 'when the second element is a valid insee code' do
let(:value) { ['01', '01457'] }
it { is_expected.to match({ id: champ.id, code_departement: '01', departement: 'Ain', external_id: '01457', value: 'Vonnas (01540)' }) }
end
context 'when the second element is not a valid insee code' do
let(:value) { ['01', 'totoro'] }
it { is_expected.to match(nil) }
end
end
context 'when the first element is not a valid departement code' do
let(:value) { ['totoro', '01457'] }
it { is_expected.to match(nil) }
end
end
context 'when the value is an array of three or more elements' do
context 'when the first element is a valid departement code' do
context 'when the second element is a valid insee code' do
let(:value) { ['01', '01457', 'hello'] }
it { is_expected.to match({ id: champ.id, code_departement: '01', departement: 'Ain', external_id: '01457', value: 'Vonnas (01540)' }) }
end
context 'when the second element is not a valid insee code' do
let(:value) { ['01', 'totoro', 'hello'] }
it { is_expected.to match(nil) }
end
end
context 'when the first element is not a valid departement code' do
let(:value) { ['totoro', '01457', 'hello'] }
it { is_expected.to match(nil) }
end
end
end
private
def departements
APIGeoService.departements.sort_by { _1[:code] }
end
end

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillDepartementTypeDeChamp, type: :model do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_departements, procedure: procedure) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
describe 'ancestors' do
subject { described_class.build(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#possible_values', vcr: { cassette_name: 'api_geo_departements' } do
let(:expected_values) {
"Un <a href=\"https://fr.wikipedia.org/wiki/Num%C3%A9rotation_des_d%C3%A9partements_fran%C3%A7ais\" target=\"_blank\">numéro de département</a><br><a title=\"Toutes les valeurs possibles — Nouvel onglet\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"/procedures/#{procedure.path}/prefill_type_de_champs/#{type_de_champ.id}\">Voir toutes les valeurs possibles</a>"
}
subject(:possible_values) { described_class.new(type_de_champ, procedure.active_revision).possible_values }
before { type_de_champ.reload }
it { expect(possible_values).to match(expected_values) }
end
end

View file

@ -2,28 +2,32 @@
RSpec.describe TypesDeChamp::PrefillDropDownListTypeDeChamp do
describe '#possible_values' do
subject(:possible_values) { described_class.new(type_de_champ).possible_values }
let(:procedure) { create(:procedure) }
subject(:possible_values) { described_class.new(type_de_champ, procedure.active_revision).possible_values }
before { type_de_champ.reload }
context "when the drop down list accepts 'other'" do
let(:type_de_champ) { build(:type_de_champ_drop_down_list, :with_other) }
let(:type_de_champ) { build(:type_de_champ_drop_down_list, :with_other, procedure: procedure) }
it {
expect(possible_values).to match(
[I18n.t("views.prefill_descriptions.edit.possible_values.drop_down_list_other_html")] + type_de_champ.drop_down_list_enabled_non_empty_options
([I18n.t("views.prefill_descriptions.edit.possible_values.drop_down_list_other_html")] + type_de_champ.drop_down_list_enabled_non_empty_options).to_sentence
)
}
end
context "when the drop down list does not accept 'other'" do
let(:type_de_champ) { build(:type_de_champ_drop_down_list) }
let(:type_de_champ) { build(:type_de_champ_drop_down_list, procedure:) }
it { expect(possible_values).to match(type_de_champ.drop_down_list_enabled_non_empty_options) }
it { expect(possible_values).to match(type_de_champ.drop_down_list_enabled_non_empty_options.to_sentence) }
end
end
describe '#example_value' do
let(:type_de_champ) { build(:type_de_champ_drop_down_list) }
subject(:example_value) { described_class.new(type_de_champ).example_value }
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_drop_down_list, procedure: procedure) }
subject(:example_value) { described_class.new(type_de_champ, procedure.active_revision).example_value }
it { expect(example_value).to eq(type_de_champ.drop_down_list_enabled_non_empty_options.first) }
end

View file

@ -0,0 +1,106 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillEpciTypeDeChamp do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_epci, procedure: procedure) }
let(:champ) { create(:champ_epci, type_de_champ: type_de_champ) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
describe 'ancestors' do
subject { described_class.new(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#all_possible_values' do
let(:expected_values) do
departements.map { |departement| "#{departement[:code]} (#{departement[:name]}) : https://geo.api.gouv.fr/epcis?codeDepartement=#{departement[:code]}" }
end
subject(:all_possible_values) { described_class.new(type_de_champ, procedure.active_revision).all_possible_values }
before do
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
it { expect(all_possible_values).to match(expected_values) }
end
describe '#example_value' do
let(:departement_code) { departements.pick(:code) }
let(:epci_code) { APIGeoService.epcis(departement_code).pick(:code) }
subject(:example_value) { described_class.new(type_de_champ, procedure.active_revision).example_value }
before do
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_epcis')
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_epcis')
end
it { is_expected.to eq([departement_code, epci_code]) }
end
describe '#to_assignable_attributes' do
subject(:to_assignable_attributes) { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
shared_examples "a transformation to" do |code_departement, value|
it { is_expected.to match({ code_departement: code_departement, value: value, id: champ.id }) }
end
context 'when the value is nil' do
let(:value) { nil }
it_behaves_like "a transformation to", nil, nil
end
context 'when the value is empty' do
let(:value) { '' }
it_behaves_like "a transformation to", nil, nil
end
context 'when the value is a string' do
let(:value) { 'hello' }
it_behaves_like "a transformation to", nil, nil
end
context 'when the value is an array of one element' do
let(:value) { ['01'] }
it_behaves_like "a transformation to", '01', nil
end
context 'when the value is an array of two elements' do
let(:value) { ['01', '200042935'] }
it_behaves_like "a transformation to", '01', '200042935'
end
context 'when the value is an array of three or more elements' do
let(:value) { ['01', '200042935', 'hello'] }
it_behaves_like "a transformation to", '01', '200042935'
end
end
private
def departements
APIGeoService.departements.sort_by { |departement| departement[:code] }
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillMultipleDropDownListTypeDeChamp do
let(:procedure) { create(:procedure) }
describe 'ancestors' do
subject { described_class.new(build(:type_de_champ_multiple_drop_down_list, procedure: procedure), procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillDropDownListTypeDeChamp) }
end
describe '#example_value' do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, drop_down_list_value: drop_down_list_value, procedure: procedure) }
subject(:example_value) { described_class.new(type_de_champ, procedure.active_revision).example_value }
context 'when the multiple drop down list has no option' do
let(:drop_down_list_value) { "" }
it { expect(example_value).to eq(nil) }
end
context 'when the multiple drop down list only has one option' do
let(:drop_down_list_value) { "value" }
it { expect(example_value).to eq("value") }
end
context 'when the multiple drop down list has two options or more' do
let(:drop_down_list_value) { "value1\r\nvalue2\r\nvalue3" }
it { expect(example_value).to eq(["value1", "value2"]) }
end
end
end

View file

@ -1,17 +1,22 @@
RSpec.describe TypesDeChamp::PrefillPaysTypeDeChamp, type: :model do
let(:type_de_champ) { build(:type_de_champ_pays) }
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_pays, procedure: procedure) }
describe 'ancestors' do
subject { described_class.build(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#possible_values' do
let(:expected_values) { APIGeoService.countries.sort_by { |country| country[:code] }.map { |country| "#{country[:code]} (#{country[:name]})" } }
subject(:possible_values) { described_class.new(type_de_champ).possible_values }
let(:expected_values) { "Un <a href=\"https://en.wikipedia.org/wiki/ISO_3166-2\" target=\"_blank\" rel=\"noopener noreferrer\">code pays ISO 3166-2</a><br><a title=\"Toutes les valeurs possibles — Nouvel onglet\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"/procedures/#{procedure.path}/prefill_type_de_champs/#{type_de_champ.id}\">Voir toutes les valeurs possibles</a>" }
subject(:possible_values) { described_class.new(type_de_champ, procedure.active_revision).possible_values }
it { expect(possible_values).to match(expected_values) }
end
before { type_de_champ.reload }
describe '#example_value' do
subject(:example_value) { described_class.new(type_de_champ).example_value }
it { expect(example_value).to eq(APIGeoService.countries.sort_by { |country| country[:code] }.first[:code]) }
it {
expect(possible_values).to match(expected_values)
}
end
end

View file

@ -1,7 +1,8 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillRegionTypeDeChamp, type: :model do
let(:type_de_champ) { build(:type_de_champ_regions) }
let(:procedure) { create(:procedure) }
let(:type_de_champ) { create(:type_de_champ_regions, procedure: procedure) }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
@ -10,15 +11,19 @@ RSpec.describe TypesDeChamp::PrefillRegionTypeDeChamp, type: :model do
end
describe 'ancestors' do
subject { described_class.build(type_de_champ) }
subject { described_class.build(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#possible_values', vcr: { cassette_name: 'api_geo_regions' } do
let(:expected_values) { APIGeoService.regions.sort_by { |region| region[:code] }.map { |region| "#{region[:code]} (#{region[:name]})" } }
subject(:possible_values) { described_class.new(type_de_champ).possible_values }
let(:expected_values) { "Un <a href=\"https://fr.wikipedia.org/wiki/R%C3%A9gion_fran%C3%A7aise\" target=\"_blank\" rel=\"noopener noreferrer\">code INSEE de région</a><br><a title=\"Toutes les valeurs possibles — Nouvel onglet\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"/procedures/#{procedure.path}/prefill_type_de_champs/#{type_de_champ.id}\">Voir toutes les valeurs possibles</a>" }
subject(:possible_values) { described_class.new(type_de_champ, procedure.active_revision).possible_values }
it { expect(possible_values).to match(expected_values) }
before { type_de_champ.reload }
it {
expect(possible_values).to eq(expected_values)
}
end
end

View file

@ -0,0 +1,78 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillRepetitionTypeDeChamp, type: :model, vcr: { cassette_name: 'api_geo_regions' } do
let(:procedure) { create(:procedure) }
let(:type_de_champ) { build(:type_de_champ_repetition, :with_types_de_champ, :with_region_types_de_champ, procedure: procedure) }
let(:champ) { create(:champ_repetition, type_de_champ: type_de_champ) }
let(:prefillable_subchamps) { TypesDeChamp::PrefillRepetitionTypeDeChamp.new(type_de_champ, procedure.active_revision).send(:prefillable_subchamps) }
let(:text_repetition) { prefillable_subchamps.first }
let(:integer_repetition) { prefillable_subchamps.second }
let(:region_repetition) { prefillable_subchamps.third }
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
describe 'ancestors' do
subject { described_class.build(type_de_champ, procedure.active_revision) }
it { is_expected.to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
describe '#possible_values' do
subject(:possible_values) { described_class.new(type_de_champ, procedure.active_revision).possible_values }
let(:expected_value) {
"Un tableau de dictionnaires avec les valeurs possibles pour chaque champ de la répétition.</br><ul><li>champ_#{text_repetition.to_typed_id_for_query}: Un texte court<br></li><li>champ_#{integer_repetition.to_typed_id_for_query}: Un nombre entier<br></li><li>champ_#{region_repetition.to_typed_id_for_query}: Un <a href=\"https://fr.wikipedia.org/wiki/R%C3%A9gion_fran%C3%A7aise\" target=\"_blank\" rel=\"noopener noreferrer\">code INSEE de région</a><br><a title=\"Toutes les valeurs possibles — Nouvel onglet\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"/procedures/#{procedure.path}/prefill_type_de_champs/#{region_repetition.id}\">Voir toutes les valeurs possibles</a></li></ul>"
}
it {
expect(possible_values).to eq(expected_value)
}
end
describe '#example_value' do
subject(:example_value) { described_class.new(type_de_champ, procedure.active_revision).example_value }
let(:expected_value) { [{ "champ_#{text_repetition.to_typed_id_for_query}" => "Texte court", "champ_#{integer_repetition.to_typed_id_for_query}" => "42", "champ_#{region_repetition.to_typed_id_for_query}" => "53" }, { "champ_#{text_repetition.to_typed_id_for_query}" => "Texte court", "champ_#{integer_repetition.to_typed_id_for_query}" => "42", "champ_#{region_repetition.to_typed_id_for_query}" => "53" }] }
it { expect(example_value).to eq(expected_value) }
end
describe '#to_assignable_attributes' do
subject(:to_assignable_attributes) { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when the value is nil' do
let(:value) { nil }
it { is_expected.to match([]) }
end
context 'when the value is empty' do
let(:value) { '' }
it { is_expected.to match([]) }
end
context 'when the value is a string' do
let(:value) { 'hello' }
it { is_expected.to match([]) }
end
context 'when the value is an array with wrong keys' do
let(:value) { ["{\"blabla\":\"value\"}", "{\"blabla\":\"value2\"}"] }
it { is_expected.to match([]) }
end
context 'when the value is an array with some wrong keys' do
let(:value) { [{ "champ_#{text_repetition.to_typed_id_for_query}" => "value", "blabla" => "value2" }, { "champ_#{integer_repetition.to_typed_id_for_query}" => "value3" }, { "blabla" => "false" }] }
it { is_expected.to match([[{ id: text_repetition.champ.first.id, value: "value" }], [{ id: integer_repetition.champ.second.id, value: "value3" }]]) }
end
context 'when the value is an array with right keys' do
let(:value) { [{ "champ_#{text_repetition.to_typed_id_for_query}" => "value" }, { "champ_#{text_repetition.to_typed_id_for_query}" => "value2" }] }
it { is_expected.to match([[{ id: text_repetition.champ.first.id, value: "value" }], [{ id: text_repetition.champ.second.id, value: "value2" }]]) }
end
end
end

View file

@ -1,36 +1,77 @@
# frozen_string_literal: true
RSpec.describe TypesDeChamp::PrefillTypeDeChamp, type: :model do
include ActionView::Helpers::UrlHelper
include ApplicationHelper
let(:procedure) { create(:procedure) }
describe '.build' do
subject(:built) { described_class.build(type_de_champ) }
subject(:built) { described_class.build(type_de_champ, procedure.active_revision) }
context 'when the type de champ is a drop_down_list' do
let(:type_de_champ) { build(:type_de_champ_drop_down_list) }
let(:type_de_champ) { build(:type_de_champ_drop_down_list, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillDropDownListTypeDeChamp) }
end
context 'when the type de champ is a multiple_drop_down_list' do
let(:type_de_champ) { build(:type_de_champ_multiple_drop_down_list, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillMultipleDropDownListTypeDeChamp) }
end
context 'when the type de champ is a pays' do
let(:type_de_champ) { build(:type_de_champ_pays) }
let(:type_de_champ) { build(:type_de_champ_pays, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillPaysTypeDeChamp) }
end
context 'when the type de champ is a regions' do
let(:type_de_champ) { build(:type_de_champ_regions) }
let(:type_de_champ) { build(:type_de_champ_regions, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillRegionTypeDeChamp) }
end
context 'when the type de champ is a repetition' do
let(:type_de_champ) { build(:type_de_champ_repetition, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillRepetitionTypeDeChamp) }
end
context 'when the type de champ is a departements' do
let(:type_de_champ) { build(:type_de_champ_departements, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillDepartementTypeDeChamp) }
end
context 'when the type de champ is a communes' do
let(:type_de_champ) { build(:type_de_champ_communes) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillCommuneTypeDeChamp) }
end
context 'when the type de champ is a epci' do
let(:type_de_champ) { build(:type_de_champ_epci, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillEpciTypeDeChamp) }
end
context 'when the type de champ is an annuaire_education' do
let(:type_de_champ) { build(:type_de_champ_annuaire_education) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillAnnuaireEducationTypeDeChamp) }
end
context 'when any other type de champ' do
let(:type_de_champ) { build(:type_de_champ_date) }
let(:type_de_champ) { build(:type_de_champ_date, procedure: procedure) }
it { expect(built).to be_kind_of(TypesDeChamp::PrefillTypeDeChamp) }
end
end
describe '.wrap' do
subject(:wrapped) { described_class.wrap([build(:type_de_champ_drop_down_list), build(:type_de_champ_email)]) }
subject(:wrapped) { described_class.wrap([build(:type_de_champ_drop_down_list, procedure: procedure), build(:type_de_champ_email, procedure: procedure)], procedure.active_revision) }
it 'wraps the collection' do
expect(wrapped.first).to be_kind_of(TypesDeChamp::PrefillDropDownListTypeDeChamp)
@ -39,51 +80,81 @@ RSpec.describe TypesDeChamp::PrefillTypeDeChamp, type: :model do
end
describe '#possible_values' do
subject(:possible_values) { described_class.build(type_de_champ).possible_values }
context 'when the type de champ is not prefillable' do
let(:type_de_champ) { build(:type_de_champ_mesri) }
it { expect(possible_values).to be_empty }
end
let(:built) { described_class.build(type_de_champ, procedure.active_revision) }
subject(:possible_values) { built.possible_values }
context 'when the type de champ is prefillable' do
let(:type_de_champ) { build(:type_de_champ_email) }
context 'when the type de champ has a description' do
let(:type_de_champ) { build(:type_de_champ_text) }
it { expect(possible_values).to match([]) }
it { expect(possible_values).to include(I18n.t("views.prefill_descriptions.edit.possible_values.#{type_de_champ.type_champ}_html")) }
end
context 'when the type de champ does not have a description' do
let(:type_de_champ) { build(:type_de_champ_mesri) }
it { expect(possible_values).not_to include(I18n.t("views.prefill_descriptions.edit.possible_values.#{type_de_champ.type_champ}_html")) }
end
describe 'too many possible values or not' do
let!(:procedure) { create(:procedure, :with_drop_down_list) }
let(:type_de_champ) { procedure.draft_types_de_champ_public.first }
let(:link_to_all_possible_values) {
link_to(
I18n.t("views.prefill_descriptions.edit.possible_values.link.text"),
Rails.application.routes.url_helpers.prefill_type_de_champ_path(procedure.path, type_de_champ),
title: new_tab_suffix(I18n.t("views.prefill_descriptions.edit.possible_values.link.title")),
**external_link_attributes
)
}
context 'when there is too many possible values' do
before { type_de_champ.drop_down_options = (1..described_class::POSSIBLE_VALUES_THRESHOLD + 1).map(&:to_s) }
it { expect(possible_values).to include(link_to_all_possible_values) }
it { expect(possible_values).not_to include(built.all_possible_values.to_sentence) }
end
context 'when there is not too many possible values' do
before { type_de_champ.drop_down_options = (1..described_class::POSSIBLE_VALUES_THRESHOLD - 1).map(&:to_s) }
it { expect(possible_values).not_to include(link_to_all_possible_values) }
it { expect(possible_values).to include(built.all_possible_values.to_sentence) }
end
end
end
context 'when the type de champ is not prefillable' do
let(:type_de_champ) { build(:type_de_champ_mesri, procedure: procedure) }
it { expect(possible_values).to be_empty }
end
end
describe '#example_value' do
subject(:example_value) { described_class.build(type_de_champ).example_value }
subject(:example_value) { described_class.build(type_de_champ, procedure.active_revision).example_value }
context 'when the type de champ is not prefillable' do
let(:type_de_champ) { build(:type_de_champ_mesri) }
let(:type_de_champ) { build(:type_de_champ_mesri, procedure: procedure) }
it { expect(example_value).to be_nil }
end
context 'when the type de champ is prefillable' do
let(:type_de_champ) { build(:type_de_champ_email) }
let(:type_de_champ) { build(:type_de_champ_email, procedure: procedure) }
it { expect(example_value).to eq(I18n.t("views.prefill_descriptions.edit.examples.#{type_de_champ.type_champ}")) }
end
end
describe '#too_many_possible_values?' do
let(:type_de_champ) { build(:type_de_champ_drop_down_list) }
subject(:too_many_possible_values) { described_class.build(type_de_champ).too_many_possible_values? }
describe '#to_assignable_attributes' do
let(:type_de_champ) { build(:type_de_champ_email, procedure: procedure) }
let(:champ) { build(:champ, type_de_champ: type_de_champ) }
let(:value) { "any@email.org" }
subject(:to_assignable_attributes) { described_class.build(type_de_champ, procedure.active_revision).to_assignable_attributes(champ, value) }
context 'when there are too many possible values' do
before { type_de_champ.drop_down_options = (1..described_class::POSSIBLE_VALUES_THRESHOLD + 1).map(&:to_s) }
it { expect(too_many_possible_values).to eq(true) }
end
context 'when there are not too many possible values' do
before { type_de_champ.drop_down_options = (1..described_class::POSSIBLE_VALUES_THRESHOLD).map(&:to_s) }
it { expect(too_many_possible_values).to eq(false) }
end
it { is_expected.to match({ id: champ.id, value: value }) }
end
end

View file

@ -15,11 +15,16 @@ describe APIEntrepriseService do
before do
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/)
.to_return(body: etablissements_body, status: etablissements_status)
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siret[0..8]}/)
.to_return(body: entreprises_body, status: entreprises_status)
end
let(:siret) { '41816609600051' }
let(:raison_sociale) { "OCTO-TECHNOLOGY" }
let(:etablissements_status) { 200 }
let(:etablissements_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') }
let(:entreprises_status) { 200 }
let(:entreprises_body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') }
let(:valid_token) { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" }
let(:procedure) { create(:procedure, api_entreprise_token: valid_token) }
let(:dossier) { create(:dossier, procedure: procedure) }
@ -35,6 +40,10 @@ describe APIEntrepriseService do
expect(subject[:siret]).to eq(siret)
end
it 'should fetch entreprise params' do
expect(subject[:entreprise_raison_sociale]).to eq(raison_sociale)
end
it_behaves_like 'schedule fetch of all etablissement params'
end

View file

@ -36,13 +36,36 @@ describe APIGeoService do
describe 'departements', vcr: { cassette_name: 'api_geo_departements' } do
it 'return sorted results' do
expect(APIGeoService.departements.size).to eq(102)
expect(APIGeoService.departements.size).to eq(110)
expect(APIGeoService.departements.first).to eq(code: '99', name: 'Etranger')
expect(APIGeoService.departements.second).to eq(code: '01', name: 'Ain')
expect(APIGeoService.departements.last).to eq(code: '976', name: 'Mayotte')
expect(APIGeoService.departements.last).to eq(code: '989', name: 'Île de Clipperton')
end
end
describe 'communes', vcr: { cassette_name: 'api_geo_communes' } do
it 'return sorted results' do
expect(APIGeoService.communes('01').size).to eq(393)
expect(APIGeoService.communes('01').first).to eq(code: '01004', name: 'Ambérieu-en-Bugey', postal_codes: ['01500'])
expect(APIGeoService.communes('01').last).to eq(code: '01457', name: 'Vonnas', postal_codes: ['01540'])
end
end
describe 'commune_name', vcr: { cassette_name: 'api_geo_communes' } do
subject { APIGeoService.commune_name('01', '01457') }
it { is_expected.to eq('Vonnas') }
end
describe 'commune_code', vcr: { cassette_name: 'api_geo_communes' } do
subject { APIGeoService.commune_code('01', 'Vonnas') }
it { is_expected.to eq('01457') }
end
describe 'commune_postal_codes', vcr: { cassette_name: 'api_geo_communes' } do
subject { APIGeoService.commune_postal_codes('01', '01457') }
it { is_expected.to eq(['01540']) }
end
describe 'epcis', vcr: { cassette_name: 'api_geo_epcis' } do
it 'return sorted results' do
expect(APIGeoService.epcis('01').size).to eq(17)

View file

@ -17,7 +17,7 @@ describe DemarchesPubliquesExportService do
typeOrganisme: "association"
},
cadreJuridiqueUrl: "un cadre juridique important",
demarcheUrl: nil,
demarcheUrl: Rails.application.routes.url_helpers.commencer_url(path: procedure.path),
dpoUrl: nil,
noticeUrl: nil,
siteWebUrl: "https://mon-site.gouv",

View file

@ -1,5 +1,5 @@
describe InstructeursImportService do
describe '#import' do
describe '#import_groupes' do
let(:procedure) { create(:procedure) }
let(:procedure_groupes) do
@ -9,7 +9,7 @@ describe InstructeursImportService do
.to_h
end
subject { described_class.import(procedure, lines) }
subject { described_class.import_groupes(procedure, lines) }
context 'nominal case' do
let(:lines) do
@ -20,8 +20,8 @@ describe InstructeursImportService do
]
end
it 'imports' do
errors = subject
it 'imports groupes' do
_, errors = subject
expect(procedure_groupes.keys).to contain_exactly("Auvergne Rhone-Alpes", "Occitanie", "défaut")
expect(procedure_groupes["Auvergne Rhone-Alpes"]).to contain_exactly("john@lennon.fr")
@ -63,7 +63,7 @@ describe InstructeursImportService do
end
it 'ignores or corrects' do
errors = subject
_, errors = subject
expect(procedure_groupes.keys).to contain_exactly("Occitanie", "défaut")
expect(procedure_groupes["Occitanie"]).to contain_exactly("paul@mccartney.uk", "ringo@starr.uk")
@ -117,7 +117,7 @@ describe InstructeursImportService do
end
it 'ignores instructeur' do
errors = subject
_, errors = subject
expect(procedure_groupes.keys).to contain_exactly("défaut")
expect(procedure_groupes["défaut"]).to be_empty
@ -126,4 +126,21 @@ describe InstructeursImportService do
end
end
end
describe '#import_instructeurs' do
let(:procedure_non_routee) { create(:procedure) }
subject { described_class.import_instructeurs(procedure_non_routee, emails) }
context 'nominal case' do
let(:emails) { [{ "email" => "john@lennon.fr" }, { "email" => "paul@mccartney.uk" }, { "email" => "ringo@starr.uk" }] }
it 'imports instructeurs' do
_, errors = subject
expect(procedure_non_routee.defaut_groupe_instructeur.instructeurs.pluck(:email)).to contain_exactly("john@lennon.fr", "paul@mccartney.uk", "ringo@starr.uk")
expect(errors).to match_array([])
end
end
end
end

View file

@ -1,13 +1,9 @@
shared_examples "the user has got a prefilled dossier, owned by themselves" do
scenario "the user has got a prefilled dossier, owned by themselves" do
siret = '41816609600051'
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}/)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/etablissements.json'))
expect(dossier.user).to eq(user)
expect(page).to have_current_path siret_dossier_path(procedure.dossiers.last)
fill_in 'Numéro SIRET', with: siret
fill_in 'Numéro SIRET', with: siret_value
click_on 'Valider'
expect(page).to have_current_path(etablissement_dossier_path(dossier))
@ -18,6 +14,18 @@ shared_examples "the user has got a prefilled dossier, owned by themselves" do
expect(page).to have_field(type_de_champ_text.libelle, with: text_value)
expect(page).to have_field(type_de_champ_phone.libelle, with: phone_value)
expect(page).to have_css('label', text: type_de_champ_phone.libelle)
expect(page).to have_field(type_de_champ_rna.libelle, with: rna_value)
expect(page).to have_field(type_de_champ_siret.libelle, with: siret_value)
expect(page).to have_css('h3', text: type_de_champ_repetition.libelle)
expect(page).to have_field(text_repetition_libelle, with: text_repetition_value)
expect(page).to have_field(integer_repetition_libelle, with: integer_repetition_value)
expect(page).to have_field(type_de_champ_datetime.libelle, with: datetime_value)
expect(page).to have_css('label', text: type_de_champ_multiple_drop_down_list.libelle)
expect(page).to have_content(multiple_drop_down_list_values.first)
expect(page).to have_content(multiple_drop_down_list_values.last)
expect(page).to have_field(type_de_champ_epci.libelle, with: epci_value.last)
expect(page).to have_field(type_de_champ_dossier_link.libelle, with: dossier_link_value)
expect(page).to have_selector("input[value='Vonnas (01540)']")
expect(page).to have_content(annuaire_education_value.last)
end
end

View file

@ -14,7 +14,22 @@ describe 'As an administrateur I wanna clone a procedure', js: true do
published_at: Time.zone.now)
login_as administrateur.user, scope: :user
end
context 'Visit all admin procedures' do
let(:download_dir) { Rails.root.join('tmp/capybara') }
let(:download_file_pattern) { download_dir.join('*.xlsx') }
scenario do
Dir[download_file_pattern].map { File.delete(_1) }
visit all_admin_procedures_path
click_on "Exporter les résultats"
Timeout.timeout(Capybara.default_max_wait_time,
Timeout::Error,
"File download timeout! can't download procedure/all.xlsx") do
sleep 0.1 until !Dir[download_file_pattern].empty?
end
end
end
context 'Cloning a procedure owned by the current admin' do
scenario do
visit admin_procedures_path

View file

@ -63,7 +63,7 @@ describe 'Inviting an expert:', js: true do
click_on 'Avis externes'
expect(page).to have_content(answered_avis.expert.email)
answered_avis.answer.split("\n").each do |answer_line|
answered_avis.answer.split("\n").map { |line| line.gsub("- ", "") }.map do |answer_line|
expect(page).to have_content(answer_line)
end
end

View file

@ -16,7 +16,7 @@ describe 'As an instructeur', js: true do
end
scenario 'I can register' do
confirmation_email = open_email(instructeur_email)
confirmation_email = emails_sent_to(instructeur_email).first
token_params = confirmation_email.body.match(/token=[^"]+/)
visit "users/activate?#{token_params}"

View file

@ -1,6 +1,6 @@
describe "procedure filters" do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, :with_type_de_champ, instructeurs: [instructeur]) }
let(:procedure) { create(:procedure, :published, :with_type_de_champ, :with_departement, :with_region, instructeurs: [instructeur]) }
let!(:type_de_champ) { procedure.active_revision.types_de_champ_public.first }
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
let!(:champ) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier.id) }
@ -94,6 +94,44 @@ describe "procedure filters" do
click_button "Ajouter le filtre"
end
describe 'with a vcr cached cassette' do
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
end
scenario "should be able to find by departements with custom enum lookup", js: true, vcr: { cassette_name: 'api_geo_departements' } do
departement_champ = new_unfollow_dossier.champs.find(&:departement?)
departement_champ.update!(value: 'Oise', external_id: '60')
departement_champ.reload
champ_select_value = "#{departement_champ.external_id} #{departement_champ.value}"
click_on 'Sélectionner un filtre'
select departement_champ.libelle, from: "Colonne"
find("select#value", visible: true)
select champ_select_value, from: "Valeur"
click_button "Ajouter le filtre"
find("select#value", visible: false) # w8 for filter to be applied
expect(page).to have_link(new_unfollow_dossier.id.to_s)
end
scenario "should be able to find by region with custom enum lookup", js: true, vcr: { cassette_name: 'api_geo_regions' } do
region_champ = new_unfollow_dossier.champs.find(&:region?)
region_champ.update!(value: 'Bretagne', external_id: '53')
region_champ.reload
click_on 'Sélectionner un filtre'
select region_champ.libelle, from: "Colonne"
find("select#value", visible: true)
select region_champ.value, from: "Valeur"
click_button "Ajouter le filtre"
find("select#value", visible: false) # w8 for filter to be applied
expect(page).to have_link(new_unfollow_dossier.id.to_s)
end
end
scenario "should be able to add and remove two filters for the same field", js: true do
add_filter(type_de_champ.libelle, champ.value)
add_filter(type_de_champ.libelle, champ_2.value)

View file

@ -5,7 +5,7 @@ describe 'As an integrator:', js: true do
before { visit "/preremplir/#{procedure.path}" }
scenario 'I can read the procedure prefilling (aka public champs)' do
expect(page).to have_content(type_de_champ.to_typed_id)
expect(page).to have_content(type_de_champ.to_typed_id_for_query)
expect(page).to have_content(I18n.t("activerecord.attributes.type_de_champ.type_champs.#{type_de_champ.type_champ}"))
expect(page).to have_content(type_de_champ.libelle)
expect(page).to have_content(type_de_champ.description)

View file

@ -1,4 +1,6 @@
describe 'Prefilling a dossier (with a GET request):' do
describe 'Prefilling a dossier (with a GET request):', js: true do
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
let(:password) { 'my-s3cure-p4ssword' }
let(:procedure) { create(:procedure, :published, opendata: true) }
@ -6,10 +8,82 @@ describe 'Prefilling a dossier (with a GET request):' do
let(:type_de_champ_text) { create(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_phone) { create(:type_de_champ_phone, procedure: procedure) }
let(:type_de_champ_rna) { create(:type_de_champ_rna, procedure: procedure) }
let(:type_de_champ_siret) { create(:type_de_champ_siret, procedure: procedure) }
let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) }
let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) }
let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) }
let(:type_de_champ_annuaire_education) { create(:type_de_champ_annuaire_education, procedure: procedure) }
let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) }
let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) }
let(:text_value) { "My Neighbor Totoro is the best movie ever" }
let(:phone_value) { "invalid phone value" }
let(:rna_value) { 'W595001988' }
let(:siret_value) { '41816609600051' }
let(:datetime_value) { "2023-02-01T10:32" }
let(:multiple_drop_down_list_values) {
[
type_de_champ_multiple_drop_down_list.drop_down_list_enabled_non_empty_options.first,
type_de_champ_multiple_drop_down_list.drop_down_list_enabled_non_empty_options.last
]
}
let(:epci_value) { ['01', '200029999'] }
let(:dossier_link_value) { '42' }
let(:commune_value) { ['01', '01457'] } # Vonnas (01540)
let(:sub_type_de_champs_repetition) { procedure.active_revision.children_of(type_de_champ_repetition) }
let(:text_repetition_libelle) { sub_type_de_champs_repetition.first.libelle }
let(:integer_repetition_libelle) { sub_type_de_champs_repetition.second.libelle }
let(:text_repetition_value) { "First repetition text" }
let(:integer_repetition_value) { "42" }
let(:annuaire_education_value) { '0050009H' }
let(:entry_path) {
commencer_path(
path: procedure.path,
"champ_#{type_de_champ_text.to_typed_id_for_query}" => text_value,
"champ_#{type_de_champ_phone.to_typed_id_for_query}" => phone_value,
"champ_#{type_de_champ_datetime.to_typed_id_for_query}" => datetime_value,
"champ_#{type_de_champ_multiple_drop_down_list.to_typed_id_for_query}" => multiple_drop_down_list_values,
"champ_#{type_de_champ_epci.to_typed_id_for_query}" => epci_value,
"champ_#{type_de_champ_dossier_link.to_typed_id_for_query}" => dossier_link_value,
"champ_#{type_de_champ_commune.to_typed_id_for_query}" => commune_value,
"champ_#{type_de_champ_siret.to_typed_id_for_query}" => siret_value,
"champ_#{type_de_champ_rna.to_typed_id_for_query}" => rna_value,
"champ_#{type_de_champ_repetition.to_typed_id_for_query}" => [
{
"champ_#{sub_type_de_champs_repetition.first.to_typed_id_for_query}": text_repetition_value,
"champ_#{sub_type_de_champs_repetition.second.to_typed_id_for_query}": integer_repetition_value
}
],
"champ_#{type_de_champ_annuaire_education.to_typed_id_for_query}" => annuaire_education_value
)
}
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\//)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/etablissements.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siret_value[0..8]}/)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/entreprises.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\//)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/associations.json'))
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_communes')
VCR.insert_cassette('api_geo_epcis')
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_communes')
VCR.eject_cassette('api_geo_epcis')
end
context 'when authenticated' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
@ -18,13 +92,7 @@ describe 'Prefilling a dossier (with a GET request):' do
before do
visit "/users/sign_in"
sign_in_with user.email, password
visit commencer_path(
path: procedure.path,
"champ_#{type_de_champ_text.to_typed_id}" => text_value,
"champ_#{type_de_champ_phone.to_typed_id}" => phone_value,
"champ_#{type_de_champ_datetime.to_typed_id}" => datetime_value
)
visit entry_path
click_on "Poursuivre mon dossier prérempli"
end
@ -65,14 +133,7 @@ describe 'Prefilling a dossier (with a GET request):' do
end
context 'when unauthenticated' do
before do
visit commencer_path(
path: procedure.path,
"champ_#{type_de_champ_text.to_typed_id}" => text_value,
"champ_#{type_de_champ_phone.to_typed_id}" => phone_value,
"champ_#{type_de_champ_datetime.to_typed_id}" => datetime_value
)
end
before { visit entry_path }
context 'when the user signs in with email and password' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do

View file

@ -1,4 +1,6 @@
describe 'Prefilling a dossier (with a POST request):' do
describe 'Prefilling a dossier (with a POST request):', js: true do
let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) }
let(:password) { 'my-s3cure-p4ssword' }
let(:procedure) { create(:procedure, :published) }
@ -6,10 +8,60 @@ describe 'Prefilling a dossier (with a POST request):' do
let(:type_de_champ_text) { create(:type_de_champ_text, procedure: procedure) }
let(:type_de_champ_phone) { create(:type_de_champ_phone, procedure: procedure) }
let(:type_de_champ_rna) { create(:type_de_champ_rna, procedure: procedure) }
let(:type_de_champ_siret) { create(:type_de_champ_siret, procedure: procedure) }
let(:type_de_champ_datetime) { create(:type_de_champ_datetime, procedure: procedure) }
let(:type_de_champ_multiple_drop_down_list) { create(:type_de_champ_multiple_drop_down_list, procedure: procedure) }
let(:type_de_champ_epci) { create(:type_de_champ_epci, procedure: procedure) }
let(:type_de_champ_annuaire_education) { create(:type_de_champ_annuaire_education, procedure: procedure) }
let(:type_de_champ_dossier_link) { create(:type_de_champ_dossier_link, procedure: procedure) }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) }
let(:type_de_champ_commune) { create(:type_de_champ_communes, procedure: procedure) }
let(:text_value) { "My Neighbor Totoro is the best movie ever" }
let(:phone_value) { "invalid phone value" }
let(:rna_value) { 'W595001988' }
let(:siret_value) { '41816609600051' }
let(:datetime_value) { "2023-02-01T10:32" }
let(:multiple_drop_down_list_values) {
[
type_de_champ_multiple_drop_down_list.drop_down_list_enabled_non_empty_options.first,
type_de_champ_multiple_drop_down_list.drop_down_list_enabled_non_empty_options.last
]
}
let(:epci_value) { ['01', '200029999'] }
let(:commune_value) { ['01', '01457'] } # Vonnas (01540)
let(:sub_type_de_champs_repetition) { procedure.active_revision.children_of(type_de_champ_repetition) }
let(:text_repetition_libelle) { sub_type_de_champs_repetition.first.libelle }
let(:integer_repetition_libelle) { sub_type_de_champs_repetition.second.libelle }
let(:text_repetition_value) { "First repetition text" }
let(:integer_repetition_value) { "42" }
let(:dossier_link_value) { '42' }
let(:annuaire_education_value) { '0050009H' }
before do
allow(Rails).to receive(:cache).and_return(memory_store)
Rails.cache.clear
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret_value}/)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/etablissements.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siret_value[0..8]}/)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/entreprises.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\//)
.to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/associations.json'))
VCR.insert_cassette('api_geo_departements')
VCR.insert_cassette('api_geo_communes')
VCR.insert_cassette('api_geo_epcis')
end
after do
VCR.eject_cassette('api_geo_departements')
VCR.eject_cassette('api_geo_communes')
VCR.eject_cassette('api_geo_epcis')
end
scenario "the user get the URL of a prefilled orphan brouillon dossier" do
dossier_url = create_and_prefill_dossier_with_post_request
@ -96,9 +148,22 @@ describe 'Prefilling a dossier (with a POST request):' do
session.post api_public_v1_dossiers_path(procedure),
headers: { "Content-Type" => "application/json" },
params: {
"champ_#{type_de_champ_text.to_typed_id}" => text_value,
"champ_#{type_de_champ_phone.to_typed_id}" => phone_value,
"champ_#{type_de_champ_datetime.to_typed_id}" => datetime_value
"champ_#{type_de_champ_text.to_typed_id_for_query}" => text_value,
"champ_#{type_de_champ_phone.to_typed_id_for_query}" => phone_value,
"champ_#{type_de_champ_rna.to_typed_id_for_query}" => rna_value,
"champ_#{type_de_champ_siret.to_typed_id_for_query}" => siret_value,
"champ_#{type_de_champ_repetition.to_typed_id_for_query}" => [
{
"champ_#{sub_type_de_champs_repetition.first.to_typed_id_for_query}": text_repetition_value,
"champ_#{sub_type_de_champs_repetition.second.to_typed_id_for_query}": integer_repetition_value
}
],
"champ_#{type_de_champ_datetime.to_typed_id_for_query}" => datetime_value,
"champ_#{type_de_champ_multiple_drop_down_list.to_typed_id_for_query}" => multiple_drop_down_list_values,
"champ_#{type_de_champ_epci.to_typed_id_for_query}" => epci_value,
"champ_#{type_de_champ_dossier_link.to_typed_id_for_query}" => dossier_link_value,
"champ_#{type_de_champ_commune.to_typed_id_for_query}" => commune_value,
"champ_#{type_de_champ_annuaire_education.to_typed_id_for_query}" => annuaire_education_value
}.to_json
JSON.parse(session.response.body)["dossier_url"].gsub("http://www.example.com", "")
end

View file

@ -14,14 +14,14 @@ describe 'instructeurs/dossiers/envoyer_dossier_block.html.haml', type: :view do
let(:potential_recipients) { [instructeur] }
it { is_expected.to match(/data-react-props.*#{instructeur.email}/) }
it { is_expected.to have_css(".button.send") }
it { is_expected.to have_css(".fr-btn") }
end
context "there is no other instructeur for the procedure" do
let(:potential_recipients) { [] }
it { is_expected.not_to have_css("select") }
it { is_expected.not_to have_css(".button.send") }
it { is_expected.not_to have_css(".fr-btn") }
it { is_expected.to have_content("Vous êtes le seul instructeur assigné sur cette démarche") }
end
end

View file

@ -26,7 +26,9 @@ describe 'instructeurs/shared/avis/_list.html.haml', type: :view do
let(:avis) { [create(:avis, :with_answer, claimant: instructeur, experts_procedure: experts_procedure)] }
it 'renders the answer formatted with newlines' do
expect(subject).to include(simple_format(avis.first.answer))
expect(subject).to have_selector(".answer-body p", text: avis.first.answer.split("\n").first)
expect(subject).to have_selector(".answer-body ul", count: 1) # avis.answer has two list item
expect(subject).to have_selector(".answer-body ul li", count: 2)
end
end