feat(demarche): create and prefill a dossier with POST request (#8233)

* add base controller for public api

* add dossiers controller with basic checks

* create the dossier

* ensure content-type is json

* prefill dossier with given values

* mark a dossier as prefilled

When a dossier is prefilled, it's allowed not to have a user.

Plus, we add a secure token to the dossier, which we will need later to set a
user after sign in / sign up.

* set user as owner of an orphan prefilled dossier

When a visitor comes from the dossier_url answered by the public api,
the dossier is orphan:
- when the user is already authenticated: they become the owner
- when the user is not authenticated: they can sign in / sign up / france_connect
and then they become the owner

So here is the procedure:
- allow to sign in / sign up / france connect when user is unauthenticated
- set dossier ownership when the dossier is orphan
- check dossier ownership when the dossier is not
- redirect to brouillon path when user is signed in and owner

* mark the dossier as prefilled when it's prefilled
(even with a GET request, because it will be useful later on, for
exmample in order to cleanup the unused prefilled dossiers)

* system spec: prefilling dossier with post request
This commit is contained in:
Sébastien Carceles 2023-01-03 14:46:10 +01:00 committed by GitHub
parent 3f4e7ab1f5
commit 20136b7ac8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 760 additions and 111 deletions

View file

@ -0,0 +1,29 @@
class API::Public::V1::BaseController < APIController
skip_forgery_protection
before_action :check_content_type_is_json
protected
def render_missing_param(param_name)
render_error("#{param_name} is missing", :bad_request)
end
def render_bad_request(error_message)
render_error(error_message, :bad_request)
end
def render_not_found(resource_name, resource_id)
render_error("#{resource_name} #{resource_id} is not found", :not_found)
end
private
def check_content_type_is_json
render_error("Content-Type should be json", :bad_request) unless request.headers['Content-Type'] == 'application/json'
end
def render_error(message, status)
render json: { error: message }, status: status
end
end

View file

@ -0,0 +1,26 @@
class API::Public::V1::DossiersController < API::Public::V1::BaseController
before_action :retrieve_procedure
def create
dossier = Dossier.new(
revision: @procedure.active_revision,
groupe_instructeur: @procedure.defaut_groupe_instructeur_for_new_dossier,
state: Dossier.states.fetch(:brouillon),
prefilled: true
)
dossier.build_default_individual
if dossier.save
dossier.prefill!(PrefillParams.new(dossier, params.to_unsafe_h).to_a)
render json: { dossier_url: commencer_url(@procedure.path, prefill_token: dossier.prefill_token) }, status: :created
else
render_bad_request(dossier.errors.full_messages.to_sentence)
end
end
private
def retrieve_procedure
@procedure = Procedure.publiees_ou_brouillons.find_by(id: params[:id])
render_not_found("procedure", params[:id]) if @procedure.blank?
end
end

View file

@ -5,12 +5,14 @@ module ProcedureContextConcern
include Devise::StoreLocationExtension include Devise::StoreLocationExtension
def restore_procedure_context def restore_procedure_context
if has_stored_procedure_path? return unless has_stored_procedure_path?
@procedure = find_procedure_in_context
if @procedure.blank? @procedure = find_procedure_in_context
invalid_procedure_context
end if @procedure.blank?
invalid_procedure_context
else
@prefill_token = find_prefill_token_in_context
end end
end end
@ -33,6 +35,11 @@ module ProcedureContextConcern
end end
end end
def find_prefill_token_in_context
uri = URI(get_stored_location_for(:user))
CGI.parse(uri.query).dig("prefill_token")&.first if uri.query
end
def invalid_procedure_context def invalid_procedure_context
clear_stored_location_for(:user) clear_stored_location_for(:user)
flash.alert = t('errors.messages.procedure_not_found') flash.alert = t('errors.messages.procedure_not_found')

View file

@ -4,6 +4,10 @@ module Users
layout 'procedure_context' layout 'procedure_context'
before_action :retrieve_prefilled_dossier, if: -> { params[:prefill_token].present? }, only: :commencer
before_action :set_prefilled_dossier_ownership, if: -> { user_signed_in? && @prefilled_dossier&.orphan? }, only: :commencer
before_action :check_prefilled_dossier_ownership, if: -> { user_signed_in? && @prefilled_dossier }, only: :commencer
def commencer def commencer
@procedure = retrieve_procedure @procedure = retrieve_procedure
return procedure_not_found if @procedure.blank? || @procedure.brouillon? return procedure_not_found if @procedure.blank? || @procedure.brouillon?
@ -74,6 +78,21 @@ module Users
Procedure.publiees.or(Procedure.brouillons).or(Procedure.closes).find_by(path: params[:path]) Procedure.publiees.or(Procedure.brouillons).or(Procedure.closes).find_by(path: params[:path])
end end
def retrieve_prefilled_dossier
@prefilled_dossier = Dossier.state_brouillon.prefilled.find_by!(prefill_token: params[:prefill_token])
end
# The prefilled dossier is not owned yet, and the user is signed in: they become the new owner
def set_prefilled_dossier_ownership
@prefilled_dossier.update!(user: current_user)
DossierMailer.with(dossier: @prefilled_dossier).notify_new_draft.deliver_later
end
# The prefilled dossier is owned by another user: raise an exception
def check_prefilled_dossier_ownership
raise ActiveRecord::RecordNotFound unless @prefilled_dossier.owned_by?(current_user)
end
def procedure_not_found def procedure_not_found
procedure = Procedure.find_by(path: params[:path]) procedure = Procedure.find_by(path: params[:path])
@ -92,7 +111,7 @@ module Users
end end
def store_user_location!(procedure) def store_user_location!(procedure)
store_location_for(:user, helpers.procedure_lien(procedure)) store_location_for(:user, helpers.procedure_lien(procedure, prefill_token: params[:prefill_token]))
end end
def generate_empty_pdf(revision) def generate_empty_pdf(revision)

View file

@ -45,7 +45,7 @@ class Users::ConfirmationsController < Devise::ConfirmationsController
end end
if procedure_from_params if procedure_from_params
commencer_path(path: procedure_from_params.path) commencer_path(path: procedure_from_params.path, prefill_token: params[:prefill_token])
elsif signed_in? elsif signed_in?
# Will try to use `stored_location_for` to find a path # Will try to use `stored_location_for` to find a path
after_sign_in_path_for(resource_name) after_sign_in_path_for(resource_name)

View file

@ -26,6 +26,7 @@ class Users::RegistrationsController < Devise::RegistrationsController
# all Devise code. # all Devise code.
# So instead we use a per-request global variable. # So instead we use a per-request global variable.
CurrentConfirmation.procedure_after_confirmation = @procedure CurrentConfirmation.procedure_after_confirmation = @procedure
CurrentConfirmation.prefill_token = @prefill_token
# Handle existing user trying to sign up again # Handle existing user trying to sign up again
existing_user = User.find_by(email: params[:user][:email]) existing_user = User.find_by(email: params[:user][:email])

View file

@ -1,9 +1,9 @@
module ProcedureHelper module ProcedureHelper
def procedure_lien(procedure) def procedure_lien(procedure, prefill_token: nil)
if procedure.brouillon? if procedure.brouillon?
commencer_test_url(path: procedure.path) commencer_test_url(path: procedure.path, prefill_token: prefill_token)
else else
commencer_url(path: procedure.path) commencer_url(path: procedure.path, prefill_token: prefill_token)
end end
end end

View file

@ -23,6 +23,7 @@ class DeviseUserMailer < Devise::Mailer
def confirmation_instructions(record, token, opts = {}) def confirmation_instructions(record, token, opts = {})
opts[:from] = NO_REPLY_EMAIL opts[:from] = NO_REPLY_EMAIL
@procedure = opts[:procedure_after_confirmation] || nil @procedure = opts[:procedure_after_confirmation] || nil
@prefill_token = opts[:prefill_token]
super super
end end
end end

View file

@ -3,10 +3,11 @@
module DossierPrefillableConcern module DossierPrefillableConcern
extend ActiveSupport::Concern extend ActiveSupport::Concern
def prefill!(champs_attributes) def prefill!(champs_public_attributes)
return if champs_attributes.empty? attr = { prefilled: true }
attr[:champs_public_attributes] = champs_public_attributes.map { |h| h.merge(prefilled: true) } if champs_public_attributes.any?
assign_attributes(champs_attributes: champs_attributes.map { |h| h.merge(prefilled: true) }) assign_attributes(attr)
save(validate: false) save(validate: false)
end end
end end

View file

@ -1,3 +1,4 @@
class CurrentConfirmation < ActiveSupport::CurrentAttributes class CurrentConfirmation < ActiveSupport::CurrentAttributes
attribute :procedure_after_confirmation attribute :procedure_after_confirmation
attribute :prefill_token
end end

View file

@ -28,6 +28,8 @@
# last_champ_updated_at :datetime # last_champ_updated_at :datetime
# last_commentaire_updated_at :datetime # last_commentaire_updated_at :datetime
# motivation :text # motivation :text
# prefill_token :string
# prefilled :boolean
# private_search_terms :text # private_search_terms :text
# processed_at :datetime # processed_at :datetime
# search_terms :text # search_terms :text
@ -70,6 +72,8 @@ class Dossier < ApplicationRecord
DAYS_AFTER_EXPIRATION = 5 DAYS_AFTER_EXPIRATION = 5
INTERVAL_EXPIRATION = "#{MONTHS_AFTER_EXPIRATION} month #{DAYS_AFTER_EXPIRATION} days" INTERVAL_EXPIRATION = "#{MONTHS_AFTER_EXPIRATION} month #{DAYS_AFTER_EXPIRATION} days"
has_secure_token :prefill_token
has_one :etablissement, dependent: :destroy has_one :etablissement, dependent: :destroy
has_one :individual, validate: false, dependent: :destroy has_one :individual, validate: false, dependent: :destroy
has_one :attestation, dependent: :destroy has_one :attestation, dependent: :destroy
@ -218,11 +222,12 @@ class Dossier < ApplicationRecord
scope :state_termine, -> { where(state: TERMINE) } scope :state_termine, -> { where(state: TERMINE) }
scope :state_not_termine, -> { where.not(state: TERMINE) } scope :state_not_termine, -> { where.not(state: TERMINE) }
scope :archived, -> { where(archived: true) } scope :archived, -> { where(archived: true) }
scope :not_archived, -> { where(archived: false) } scope :not_archived, -> { where(archived: false) }
scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) } scope :prefilled, -> { where(prefilled: true) }
scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) } scope :hidden_by_user, -> { where.not(hidden_by_user_at: nil) }
scope :visible_by_user, -> { where(for_procedure_preview: false).or(where(for_procedure_preview: nil)).where(hidden_by_user_at: nil) } scope :hidden_by_administration, -> { where.not(hidden_by_administration_at: nil) }
scope :visible_by_user, -> { where(for_procedure_preview: false).or(where(for_procedure_preview: nil)).where(hidden_by_user_at: nil) }
scope :visible_by_administration, -> { scope :visible_by_administration, -> {
state_not_brouillon state_not_brouillon
.where(hidden_by_administration_at: nil) .where(hidden_by_administration_at: nil)
@ -435,7 +440,7 @@ class Dossier < ApplicationRecord
after_save :send_web_hook after_save :send_web_hook
validates :user, presence: true, if: -> { deleted_user_email_never_send.nil? } validates :user, presence: true, if: -> { deleted_user_email_never_send.nil? }, unless: -> { prefilled }
validates :individual, presence: true, if: -> { revision.procedure.for_individual? } validates :individual, presence: true, if: -> { revision.procedure.for_individual? }
validates :groupe_instructeur, presence: true, if: -> { !brouillon? } validates :groupe_instructeur, presence: true, if: -> { !brouillon? }
@ -718,6 +723,17 @@ class Dossier < ApplicationRecord
end end
end end
def orphan?
prefilled? && user.nil?
end
def owned_by?(a_user)
return false if a_user.nil?
return false if orphan?
user == a_user
end
def log_operations? def log_operations?
!procedure.brouillon? && !brouillon? !procedure.brouillon? && !brouillon?
end end

View file

@ -206,7 +206,7 @@ class Procedure < ApplicationRecord
scope :brouillons, -> { where(aasm_state: :brouillon) } scope :brouillons, -> { where(aasm_state: :brouillon) }
scope :publiees, -> { where(aasm_state: :publiee) } scope :publiees, -> { where(aasm_state: :publiee) }
scope :publiees_ou_brouillons, -> { publiees.or(brouillons) } scope :publiees_ou_brouillons, -> { where(aasm_state: [:publiee, :brouillon]) }
scope :closes, -> { where(aasm_state: [:close, :depubliee]) } scope :closes, -> { where(aasm_state: [:close, :depubliee]) }
scope :opendata, -> { where(opendata: true) } scope :opendata, -> { where(opendata: true) }
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :depubliee]) } scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :depubliee]) }

View file

@ -80,6 +80,7 @@ class User < ApplicationRecord
# Make our procedure_after_confirmation available to the Mailer # Make our procedure_after_confirmation available to the Mailer
opts[:procedure_after_confirmation] = CurrentConfirmation.procedure_after_confirmation opts[:procedure_after_confirmation] = CurrentConfirmation.procedure_after_confirmation
opts[:prefill_token] = CurrentConfirmation.prefill_token
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts) send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
end end

View file

@ -3,12 +3,12 @@
.commencer.form .commencer.form
- if !user_signed_in? - if !user_signed_in?
%h2.huge-title= t('views.commencer.show.start_procedure') %h2.huge-title= t('views.commencer.show.start_procedure')
= render partial: 'shared/france_connect_login', locals: { url: commencer_france_connect_path(path: @procedure.path) } = render partial: 'shared/france_connect_login', locals: { url: commencer_france_connect_path(path: @procedure.path, prefill_token: @prefilled_dossier&.prefill_token) }
= link_to commencer_sign_up_path(path: @procedure.path), class: 'fr-btn fr-btn--lg fr-my-2w' do = link_to commencer_sign_up_path(path: @procedure.path, prefill_token: @prefilled_dossier&.prefill_token), class: 'fr-btn fr-btn--lg fr-my-2w' do
= t('views.shared.account.create') = t('views.shared.account.create')
%span.optional-on-small-screens.fr-ml-1v %span.optional-on-small-screens.fr-ml-1v
#{APPLICATION_NAME} #{APPLICATION_NAME}
= link_to t('views.shared.account.already_user'), commencer_sign_in_path(path: @procedure.path), class: 'fr-btn fr-btn--secondary fr-btn--lg fr-my-2w' = link_to t('views.shared.account.already_user'), commencer_sign_in_path(path: @procedure.path, prefill_token: @prefilled_dossier&.prefill_token), class: 'fr-btn fr-btn--secondary fr-btn--lg fr-my-2w'
- else - else
- revision = @revision.draft? ? @revision : @procedure.revisions.where.not(id: @procedure.draft_revision_id) - revision = @revision.draft? ? @revision : @procedure.revisions.where.not(id: @procedure.draft_revision_id)
@ -19,6 +19,13 @@
- if dossiers.empty? - if dossiers.empty?
= link_to t('views.commencer.show.start_procedure'), url_for_new_dossier(@revision), class: 'fr-btn fr-btn--lg fr-my-2w' = link_to t('views.commencer.show.start_procedure'), url_for_new_dossier(@revision), class: 'fr-btn fr-btn--lg fr-my-2w'
- elsif @prefilled_dossier
%h2.huge-title= t('views.commencer.show.prefilled_draft')
%p
= t('views.commencer.show.prefilled_draft_detail_html', time_ago: time_ago_in_words(@prefilled_dossier.created_at), procedure: @prefilled_dossier.procedure.libelle)
= link_to t('views.commencer.show.continue_file'), brouillon_dossier_path(@prefilled_dossier), class: 'fr-btn fr-btn--lg fr-my-2w'
= link_to t('views.commencer.show.start_new_file'), url_for_new_dossier(@revision), class: 'fr-btn fr-btn--lg fr-btn--secondary fr-my-2w'
- elsif drafts.size == 1 && not_drafts.empty? - elsif drafts.size == 1 && not_drafts.empty?
- dossier = drafts.first - dossier = drafts.first
%h2.huge-title= t('views.commencer.show.already_draft') %h2.huge-title= t('views.commencer.show.already_draft')

View file

@ -6,7 +6,7 @@
%p %p
Pour activer votre compte sur #{APPLICATION_NAME}, veuillez cliquer sur le lien suivant : Pour activer votre compte sur #{APPLICATION_NAME}, veuillez cliquer sur le lien suivant :
- link = confirmation_url(@user, confirmation_token: @token, procedure_id: @procedure&.id) - link = confirmation_url(@user, confirmation_token: @token, procedure_id: @procedure&.id, prefill_token: @prefill_token)
= link_to(link, link) = link_to(link, link)
- else - else

View file

@ -92,6 +92,8 @@ en:
start_procedure: Start the procedure start_procedure: Start the procedure
existing_dossiers: You already have files for this procedure existing_dossiers: You already have files for this procedure
show_dossiers: View my current files show_dossiers: View my current files
prefilled_draft: "You have a prefilled file"
prefilled_draft_detail_html: "You prefilled a file for the \"%{procedure}\" procedure <strong>%{time_ago} ago</strong>"
already_draft: "You already started to fill a file" already_draft: "You already started to fill a file"
already_draft_detail_html: "You started to fill a file for the \"%{procedure}\" procedure <strong>%{time_ago} ago</strong>" already_draft_detail_html: "You started to fill a file for the \"%{procedure}\" procedure <strong>%{time_ago} ago</strong>"
already_not_draft: "You already submitted a file" already_not_draft: "You already submitted a file"

View file

@ -82,6 +82,8 @@ fr:
start_procedure: Commencer la démarche start_procedure: Commencer la démarche
existing_dossiers: Vous avez déjà des dossiers pour cette démarche existing_dossiers: Vous avez déjà des dossiers pour cette démarche
show_dossiers: Voir mes dossiers en cours show_dossiers: Voir mes dossiers en cours
prefilled_draft: "Vous avez un dossier prérempli"
prefilled_draft_detail_html: "Il y a <strong>%{time_ago}</strong>, vous avez prérempli un dossier sur la démarche « %{procedure} »."
already_draft: "Vous avez déjà commencé à remplir un dossier" already_draft: "Vous avez déjà commencé à remplir un dossier"
already_draft_detail_html: "Il y a <strong>%{time_ago}</strong>, vous avez commencé à remplir un dossier sur la démarche « %{procedure} »." already_draft_detail_html: "Il y a <strong>%{time_ago}</strong>, vous avez commencé à remplir un dossier sur la démarche « %{procedure} »."
already_not_draft: "Vous avez déjà déposé un dossier" already_not_draft: "Vous avez déjà déposé un dossier"

View file

@ -263,7 +263,11 @@ Rails.application.routes.draw do
namespace :public do namespace :public do
namespace :v1 do namespace :v1 do
resources :dossiers, only: :create resources :demarches, only: [] do
member do
resources :dossiers, only: :create
end
end
end end
end end
end end

View file

@ -0,0 +1,6 @@
class AddPrefillFieldsToDossiers < ActiveRecord::Migration[6.1]
def change
add_column :dossiers, :prefill_token, :string
add_column :dossiers, :prefilled, :boolean
end
end

View file

@ -0,0 +1,7 @@
class AddPrefillTokenIndexToDossiers < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def change
add_index :dossiers, :prefill_token, unique: true, algorithm: :concurrently
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_12_05_144624) do ActiveRecord::Schema.define(version: 2022_12_13_084442) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto" enable_extension "pgcrypto"
@ -298,13 +298,13 @@ ActiveRecord::Schema.define(version: 2022_12_05_144624) do
t.boolean "automatic_operation", default: false, null: false t.boolean "automatic_operation", default: false, null: false
t.bigint "bill_signature_id" t.bigint "bill_signature_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.jsonb "data"
t.text "digest" t.text "digest"
t.bigint "dossier_id" t.bigint "dossier_id"
t.datetime "executed_at" t.datetime "executed_at"
t.datetime "keep_until" t.datetime "keep_until"
t.string "operation", null: false t.string "operation", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.jsonb "data"
t.index ["bill_signature_id"], name: "index_dossier_operation_logs_on_bill_signature_id" t.index ["bill_signature_id"], name: "index_dossier_operation_logs_on_bill_signature_id"
t.index ["dossier_id"], name: "index_dossier_operation_logs_on_dossier_id" t.index ["dossier_id"], name: "index_dossier_operation_logs_on_dossier_id"
t.index ["keep_until"], name: "index_dossier_operation_logs_on_keep_until" t.index ["keep_until"], name: "index_dossier_operation_logs_on_keep_until"
@ -363,6 +363,8 @@ ActiveRecord::Schema.define(version: 2022_12_05_144624) do
t.datetime "last_commentaire_updated_at" t.datetime "last_commentaire_updated_at"
t.text "motivation" t.text "motivation"
t.bigint "parent_dossier_id" t.bigint "parent_dossier_id"
t.string "prefill_token"
t.boolean "prefilled"
t.string "private_search_terms" t.string "private_search_terms"
t.datetime "processed_at" t.datetime "processed_at"
t.bigint "revision_id" t.bigint "revision_id"
@ -376,6 +378,7 @@ ActiveRecord::Schema.define(version: 2022_12_05_144624) do
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id" t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id" t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at" t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"
t.index ["prefill_token"], name: "index_dossiers_on_prefill_token", unique: true
t.index ["revision_id"], name: "index_dossiers_on_revision_id" t.index ["revision_id"], name: "index_dossiers_on_revision_id"
t.index ["state"], name: "index_dossiers_on_state" t.index ["state"], name: "index_dossiers_on_state"
t.index ["user_id"], name: "index_dossiers_on_user_id" t.index ["user_id"], name: "index_dossiers_on_user_id"
@ -807,6 +810,7 @@ ActiveRecord::Schema.define(version: 2022_12_05_144624) do
t.datetime "reset_password_sent_at" t.datetime "reset_password_sent_at"
t.string "reset_password_token" t.string "reset_password_token"
t.integer "sign_in_count", default: 0, null: false t.integer "sign_in_count", default: 0, null: false
t.boolean "team_account", default: false
t.string "unlock_token" t.string "unlock_token"
t.datetime "updated_at" t.datetime "updated_at"
t.index ["email"], name: "index_super_admins_on_email", unique: true t.index ["email"], name: "index_super_admins_on_email", unique: true
@ -885,7 +889,6 @@ ActiveRecord::Schema.define(version: 2022_12_05_144624) do
t.string "reset_password_token" t.string "reset_password_token"
t.integer "sign_in_count", default: 0, null: false t.integer "sign_in_count", default: 0, null: false
t.string "siret" t.string "siret"
t.boolean "team_account", default: false
t.text "unconfirmed_email" t.text "unconfirmed_email"
t.string "unlock_token" t.string "unlock_token"
t.datetime "updated_at" t.datetime "updated_at"

View file

@ -0,0 +1,135 @@
RSpec.describe API::Public::V1::DossiersController, type: :controller do
include Rails.application.routes.url_helpers
describe '#create' do
# Request prototype:
# curl --request POST 'http://localhost:3000/api/public/v1/demarches/2/dossiers' \
# --header 'Content-Type: application/json' \
# --data '{"champ_Q2hhbXAtMjI=": "personne@fournisseur.fr"}'
context 'when the request content type is json' do
let(:params) { { id: procedure.id } }
subject(:create_request) do
request.headers["Content-Type"] = "application/json"
post :create, params: params
end
shared_examples 'the procedure is found' do
context 'when the dossier can be saved' do
it { expect(create_request).to have_http_status(:created) }
it { expect { create_request }.to change { Dossier.count }.by(1) }
it "marks the created dossier as prefilled" do
create_request
expect(Dossier.last.prefilled).to eq(true)
end
it "creates the dossier without a user" do
create_request
expect(Dossier.last.user).to eq(nil)
end
it "responds with the brouillon dossier path" do
create_request
expect(JSON.parse(response.body)["dossier_url"]).to eq(
"http://test.host#{commencer_path(procedure.path, prefill_token: Dossier.last.prefill_token)}"
)
end
context 'when prefill values are given' do
let!(:type_de_champ_1) { create(:type_de_champ_text, procedure: procedure) }
let(:value_1) { "any value" }
let!(:type_de_champ_2) { create(:type_de_champ_textarea, procedure: procedure) }
let(:value_2) { "another value" }
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
}
}
it "prefills the dossier's champs with the given values" do
create_request
dossier = Dossier.last
expect(find_champ_by_stable_id(dossier, type_de_champ_1.stable_id).value).to eq(value_1)
expect(find_champ_by_stable_id(dossier, type_de_champ_2.stable_id).value).to eq(value_2)
end
end
end
context 'when the dossier can not be saved' do
before do
allow_any_instance_of(Dossier).to receive(:save).and_return(false)
allow_any_instance_of(Dossier).to receive(:errors).and_return(
ActiveModel::Errors.new(Dossier.new).tap { |e| e.add(:base, "something went wrong") }
)
create_request
end
it { expect(response).to have_http_status(:bad_request) }
it { expect(response).to have_failed_with("something went wrong") }
end
end
shared_examples 'the procedure is not found' do
before { create_request }
it { expect(response).to have_http_status(:not_found) }
it { expect(response).to have_failed_with("procedure #{procedure.id} is not found") }
end
context 'when the procedure is found' do
context 'when the procedure is publiee' do
it_behaves_like 'the procedure is found' do
let(:procedure) { create(:procedure, :published) }
end
end
context 'when the procedure is brouillon' do
it_behaves_like 'the procedure is found' do
let(:procedure) { create(:procedure, :draft) }
end
end
context 'when the procedure is not publiee and not brouillon' do
it_behaves_like 'the procedure is not found' do
let(:procedure) { create(:procedure, :closed) }
end
end
end
context 'when the procedure is not found' do
it_behaves_like 'the procedure is not found' do
let(:procedure) { double(Procedure, id: -1) }
end
end
end
context 'when the request content type is not json' do
subject(:create_request) do
request.headers["Content-Type"] = "application/xhtml+xml"
post :create, params: { id: 0 }
end
before { create_request }
it { expect(response).to have_http_status(:bad_request) }
it { expect(response).to have_failed_with("Content-Type should be json") }
end
end
private
def find_champ_by_stable_id(dossier, stable_id)
dossier.champs_public.joins(:type_de_champ).find_by(types_de_champ: { stable_id: stable_id })
end
end

View file

@ -69,6 +69,19 @@ RSpec.describe ProcedureContextConcern, type: :controller do
expect(subject.status).to eq 200 expect(subject.status).to eq 200
expect(assigns(:procedure)).to eq test_procedure expect(assigns(:procedure)).to eq test_procedure
end end
context 'when a prefill token has been stored' do
let(:dossier) { create :dossier, :prefilled, procedure: test_procedure }
before do
controller.store_location_for(:user, commencer_test_path(path: test_procedure.path, prefill_token: dossier.prefill_token))
end
it 'succeeds, and assigns the prefill token on the controller' do
expect(subject.status).to eq 200
expect(assigns(:prefill_token)).to eq dossier.prefill_token
end
end
end end
context 'when the stored procedure is published' do context 'when the stored procedure is published' do
@ -82,6 +95,19 @@ RSpec.describe ProcedureContextConcern, type: :controller do
expect(subject.status).to eq 200 expect(subject.status).to eq 200
expect(assigns(:procedure)).to eq published_procedure expect(assigns(:procedure)).to eq published_procedure
end end
context 'when a prefill token has been stored' do
let(:dossier) { create :dossier, :prefilled, procedure: published_procedure }
before do
controller.store_location_for(:user, commencer_path(path: published_procedure.path, prefill_token: dossier.prefill_token))
end
it 'succeeds, and assigns the prefill token on the controller' do
expect(subject.status).to eq 200
expect(assigns(:prefill_token)).to eq dossier.prefill_token
end
end
end end
end end
end end

View file

@ -72,6 +72,85 @@ describe Users::CommencerController, type: :controller do
expect(subject).to redirect_to(commencer_path(path: replaced_by_procedure.path)) expect(subject).to redirect_to(commencer_path(path: replaced_by_procedure.path))
end end
end end
context 'when a dossier has been prefilled' do
let(:dossier) { create(:dossier, :brouillon, :prefilled, user: user) }
let(:path) { dossier.procedure.path }
subject { get :commencer, params: { path: path, prefill_token: dossier.prefill_token } }
shared_examples 'a prefilled brouillon dossier retriever' do
context 'when the dossier is a prefilled brouillon and the prefill token is present' do
it 'retrieves the dossier' do
subject
expect(assigns(:prefilled_dossier)).to eq(dossier)
end
end
context 'when the dossier is not prefilled' do
before do
dossier.prefilled = false
dossier.save(validate: false)
end
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
context 'when the dossier is not a brouillon' do
before { dossier.en_construction! }
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
context 'when the prefill token does not match any dossier' do
before { dossier.prefill_token = "totoro" }
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
end
context 'when the user is unauthenticated' do
let(:user) { nil }
it_behaves_like 'a prefilled brouillon dossier retriever'
end
context 'when the user is authenticated' do
context 'when the dossier already has an owner' do
let(:user) { create(:user) }
context 'when the user is the dossier owner' do
before { sign_in user }
it_behaves_like 'a prefilled brouillon dossier retriever'
end
context 'when the user is not the dossier owner' do
before { sign_in create(:user) }
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
end
context 'when the dossier does not have an owner yet' do
let(:user) { nil }
let(:newly_authenticated_user) { create(:user) }
before { sign_in newly_authenticated_user }
it { expect { subject }.to change { dossier.reload.user }.from(nil).to(newly_authenticated_user) }
it 'sends the notify_new_draft email' do
expect { perform_enqueued_jobs { subject } }.to change { ActionMailer::Base.deliveries.count }.by(1)
dossier = Dossier.last
mail = ActionMailer::Base.deliveries.last
expect(mail.subject).to eq("Retrouvez votre brouillon pour la démarche « #{dossier.procedure.libelle} »")
expect(mail.html_part.body).to include(dossier_path(dossier))
end
end
end
end
end end
describe '#commencer_test' do describe '#commencer_test' do
@ -105,6 +184,13 @@ describe Users::CommencerController, type: :controller do
end end
end end
shared_examples 'a prefill token storage' do
it 'stores the prefill token' do
subject
expect(controller.stored_location_for(:user)).to include('prefill_token')
end
end
describe '#sign_in' do describe '#sign_in' do
context 'for a published procedure' do context 'for a published procedure' do
subject { get :sign_in, params: { path: published_procedure.path } } subject { get :sign_in, params: { path: published_procedure.path } }
@ -115,6 +201,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(new_user_session_path) } it { expect(subject).to redirect_to(new_user_session_path) }
context 'when a prefill token is given' do
subject { get :sign_in, params: { path: published_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'for a draft procedure' do context 'for a draft procedure' do
@ -126,6 +218,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(new_user_session_path) } it { expect(subject).to redirect_to(new_user_session_path) }
context 'when a prefill token is given' do
subject { get :sign_in, params: { path: draft_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'when the path doesnt exist' do context 'when the path doesnt exist' do
@ -147,6 +245,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(new_user_registration_path) } it { expect(subject).to redirect_to(new_user_registration_path) }
context 'when a prefill token is given' do
subject { get :sign_up, params: { path: published_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'for a draft procedure' do context 'for a draft procedure' do
@ -158,6 +262,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(new_user_registration_path) } it { expect(subject).to redirect_to(new_user_registration_path) }
context 'when a prefill token is given' do
subject { get :sign_up, params: { path: draft_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'when the path doesnt exist' do context 'when the path doesnt exist' do
@ -179,6 +289,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(france_connect_particulier_path) } it { expect(subject).to redirect_to(france_connect_particulier_path) }
context 'when a prefill token is given' do
subject { get :france_connect, params: { path: published_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'for a draft procedure' do context 'for a draft procedure' do
@ -190,6 +306,12 @@ describe Users::CommencerController, type: :controller do
end end
it { expect(subject).to redirect_to(france_connect_particulier_path) } it { expect(subject).to redirect_to(france_connect_particulier_path) }
context 'when a prefill token is given' do
subject { get :france_connect, params: { path: draft_procedure.path, prefill_token: 'prefill_token' } }
it_behaves_like 'a prefill token storage'
end
end end
context 'when the path doesnt exist' do context 'when the path doesnt exist' do

View file

@ -256,5 +256,9 @@ FactoryBot.define do
dossier.save! dossier.save!
end end
end end
trait :prefilled do
prefilled { true }
end
end end
end end

View file

@ -8,6 +8,10 @@ class DeviseUserMailerPreview < ActionMailer::Preview
DeviseUserMailer.confirmation_instructions(user, "faketoken", {}) DeviseUserMailer.confirmation_instructions(user, "faketoken", {})
end end
def confirmation_instructions___with_procedure_and_prefill_token
DeviseUserMailer.confirmation_instructions(user, "faketoken", procedure_after_confirmation: procedure, prefill_token: "prefill_token")
end
def reset_password_instructions def reset_password_instructions
DeviseUserMailer.reset_password_instructions(user, "faketoken", {}) DeviseUserMailer.reset_password_instructions(user, "faketoken", {})
end end

View file

@ -8,12 +8,19 @@ RSpec.describe DossierPrefillableConcern do
subject(:fill) { dossier.prefill!(values); dossier.reload } subject(:fill) { dossier.prefill!(values); dossier.reload }
shared_examples 'a dossier marked as prefilled' do
it 'marks the dossier as prefilled' do
expect { fill }.to change { dossier.reload.prefilled }.from(nil).to(true)
end
end
context 'when champs_public_attributes is empty' do context 'when champs_public_attributes is empty' do
let(:values) { [] } let(:values) { [] }
it "does nothing" do it_behaves_like 'a dossier marked as prefilled'
expect(dossier).not_to receive(:save)
fill it "doesn't change champs_public" do
expect { fill }.not_to change { dossier.champs_public.to_a }
end end
end end
@ -30,6 +37,8 @@ RSpec.describe DossierPrefillableConcern do
let(:values) { [{ id: champ_id_1, value: value_1 }, { id: champ_id_2, value: value_2 }] } let(:values) { [{ id: champ_id_1, value: value_1 }, { id: champ_id_2, value: value_2 }] }
it_behaves_like 'a dossier marked as prefilled'
it "updates the champs with the new values and mark them as prefilled" do it "updates the champs with the new values and mark them as prefilled" do
fill fill
@ -48,6 +57,8 @@ RSpec.describe DossierPrefillableConcern do
let(:values) { [{ id: champ_id, value: value }] } let(:values) { [{ id: champ_id, value: value }] }
it_behaves_like 'a dossier marked as prefilled'
it "still updates the champ" do it "still updates the champ" do
expect { fill }.to change { dossier.champs_public.first.value }.from(nil).to(value) expect { fill }.to change { dossier.champs_public.first.value }.from(nil).to(value)
end end

View file

@ -25,6 +25,20 @@ describe Dossier do
subject(:dossier) { create(:dossier, procedure: procedure) } subject(:dossier) { create(:dossier, procedure: procedure) }
it { is_expected.to validate_presence_of(:individual) } it { is_expected.to validate_presence_of(:individual) }
it { is_expected.to validate_presence_of(:user) }
context 'when dossier has deleted_user_email_never_send' do
subject(:dossier) { create(:dossier, procedure: procedure, deleted_user_email_never_send: "seb@totoro.org") }
it { is_expected.not_to validate_presence_of(:user) }
end
context 'when dossier is prefilled' do
subject(:dossier) { create(:dossier, procedure: procedure, prefilled: true) }
it { is_expected.not_to validate_presence_of(:user) }
end
end end
describe 'with_champs' do describe 'with_champs' do
@ -1928,6 +1942,70 @@ describe Dossier do
it { is_expected.to belong_to(:batch_operation).optional } it { is_expected.to belong_to(:batch_operation).optional }
end end
describe '#orphan?' do
subject(:orphan) { dossier.orphan? }
context 'when the dossier is prefilled' do
context 'when the dossier has a user' do
let(:dossier) { build(:dossier, :prefilled) }
it { expect(orphan).to be_falsey }
end
context 'when the dossier does not have a user' do
let(:dossier) { build(:dossier, :prefilled, user: nil) }
it { expect(orphan).to be_truthy }
end
end
context 'when the dossier is not prefilled' do
context 'when the dossier has a user' do
let(:dossier) { build(:dossier) }
it { expect(orphan).to be_falsey }
end
context 'when the dossier does not have a user' do
let(:dossier) { build(:dossier, user: nil) }
it { expect(orphan).to be_falsey }
end
end
end
describe '#owned_by?' do
subject(:owned_by) { dossier.owned_by?(user) }
context 'when the dossier is orphan' do
let(:dossier) { build(:dossier, user: nil) }
let(:user) { build(:user) }
it { expect(owned_by).to be_falsey }
end
context 'when the given user is nil' do
let(:dossier) { build(:dossier) }
let(:user) { nil }
it { expect(owned_by).to be_falsey }
end
context 'when the dossier has a user and it is not the given user' do
let(:dossier) { build(:dossier) }
let(:user) { build(:user) }
it { expect(owned_by).to be_falsey }
end
context 'when the dossier has a user and it is the given user' do
let(:dossier) { build(:dossier, user: user) }
let(:user) { build(:user) }
it { expect(owned_by).to be_truthy }
end
end
private private
def count_for_month(processed_by_month, month) def count_for_month(processed_by_month, month)

View file

@ -0,0 +1,5 @@
RSpec::Matchers.define :have_failed_with do |expected|
match do |response|
JSON.parse(response.body).with_indifferent_access.dig(:error) == expected
end
end

View file

@ -0,0 +1,22 @@
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
click_on 'Valider'
expect(page).to have_current_path(etablissement_dossier_path(dossier))
expect(page).to have_content('OCTO TECHNOLOGY')
click_on 'Continuer avec ces informations'
expect(page).to have_current_path(brouillon_dossier_path(dossier))
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('.field_with_errors', text: type_de_champ_phone.libelle)
end
end

View file

@ -0,0 +1,87 @@
describe 'Prefilling a dossier (with a GET request):' do
let(:password) { 'my-s3cure-p4ssword' }
let(:procedure) { create(:procedure, :published, opendata: true) }
let(:dossier) { procedure.dossiers.last }
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(:text_value) { "My Neighbor Totoro is the best movie ever" }
let(:phone_value) { "invalid phone value" }
context 'when authenticated' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user) { create(:user, password: password) }
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
)
click_on "Commencer la démarche"
end
end
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
)
end
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
let!(:user) { create(:user, password: password) }
before do
click_on "Jai déjà un compte"
sign_in_with user.email, password
click_on "Commencer la démarche"
end
end
end
context 'when the user signs up with email and password' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user_email) { generate :user_email }
let(:user) { User.find_by(email: user_email) }
before do
click_on "Créer un compte #{APPLICATION_NAME}"
sign_up_with user_email, password
expect(page).to have_content "nous avons besoin de vérifier votre adresse #{user_email}"
click_confirmation_link_for user_email
expect(page).to have_content('Votre compte a bien été confirmé.')
click_on "Commencer la démarche"
end
end
end
context 'when the user signs up with FranceConnect' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user) { User.last }
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: "c0d3"))
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(build(:france_connect_information))
page.find('.fr-connect').click
click_on "Commencer la démarche"
end
end
end
end
end

View file

@ -0,0 +1,102 @@
describe 'Prefilling a dossier (with a POST request):' do
let(:password) { 'my-s3cure-p4ssword' }
let(:procedure) { create(:procedure, :published) }
let(:dossier) { procedure.dossiers.last }
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(:text_value) { "My Neighbor Totoro is the best movie ever" }
let(:phone_value) { "invalid phone value" }
scenario "the user get the URL of a prefilled orphan brouillon dossier" do
dossier_url = create_and_prefill_dossier_with_post_request
expect(dossier_url).to eq(commencer_path(procedure.path, prefill_token: dossier.prefill_token))
end
describe 'visit the dossier URL' do
context 'when authenticated' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user) { create(:user, password: password) }
before do
visit "/users/sign_in"
sign_in_with user.email, password
visit create_and_prefill_dossier_with_post_request
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Continuer à remplir mon dossier'
end
end
end
context 'when unauthenticated' do
before { visit create_and_prefill_dossier_with_post_request }
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
let(:user) { create(:user, password: password) }
before do
click_on "Jai déjà un compte"
sign_in_with user.email, password
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Continuer à remplir mon dossier'
end
end
end
context 'when the user signs up with email and password' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user_email) { generate :user_email }
let(:user) { User.find_by(email: user_email) }
before do
click_on "Créer un compte #{APPLICATION_NAME}"
sign_up_with user_email, password
expect(page).to have_content "nous avons besoin de vérifier votre adresse #{user_email}"
click_confirmation_link_for user_email
expect(page).to have_content('Votre compte a bien été confirmé.')
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Continuer à remplir mon dossier'
end
end
end
context 'when the user signs up with FranceConnect' do
it_behaves_like "the user has got a prefilled dossier, owned by themselves" do
let(:user) { User.last }
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: "c0d3"))
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(build(:france_connect_information))
page.find('.fr-connect').click
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Continuer à remplir mon dossier'
end
end
end
end
end
private
def create_and_prefill_dossier_with_post_request
session = ActionDispatch::Integration::Session.new(Rails.application)
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
}.to_json
JSON.parse(session.response.body)["dossier_url"].gsub("http://www.example.com", "")
end
end

View file

@ -1,80 +0,0 @@
describe 'Prefilling a dossier:' do
let(:password) { 'my-s3cure-p4ssword' }
let(:siret) { '41816609600051' }
let(:procedure) { create(:procedure, :published, opendata: true) }
let(:dossier) { procedure.dossiers.last }
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(:text_value) { "My Neighbor Totoro is the best movie ever" }
let(:phone_value) { "invalid phone value" }
before do
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'))
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
)
end
shared_examples "the user has got a prefilled dossier" do
scenario "the user has got a prefilled dossier" do
click_on "Commencer la démarche"
expect(page).to have_current_path siret_dossier_path(procedure.dossiers.last)
fill_in 'Numéro SIRET', with: siret
click_on 'Valider'
expect(page).to have_current_path(etablissement_dossier_path(dossier))
expect(page).to have_content('OCTO TECHNOLOGY')
click_on 'Continuer avec ces informations'
expect(page).to have_current_path(brouillon_dossier_path(dossier))
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('.field_with_errors', text: type_de_champ_phone.libelle)
end
end
context 'when the user signs in with email and password' do
it_behaves_like "the user has got a prefilled dossier" do
let!(:user) { create(:user, password: password) }
before do
click_on "Jai déjà un compte"
sign_in_with user.email, password
end
end
end
context 'when the user signs up with email and password' do
it_behaves_like "the user has got a prefilled dossier" do
let(:user_email) { generate :user_email }
before do
click_on "Créer un compte #{APPLICATION_NAME}"
sign_up_with user_email, password
expect(page).to have_content "nous avons besoin de vérifier votre adresse #{user_email}"
click_confirmation_link_for user_email
expect(page).to have_content('Votre compte a bien été confirmé.')
end
end
end
context 'when the user signs up with FranceConnect' do
it_behaves_like "the user has got a prefilled dossier" do
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: "c0d3"))
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(build(:france_connect_information))
page.find('.fr-connect').click
end
end
end
end