accessibilite(pages-authentification): evolutions des pages de connexion/creation de compte pour respecter le DSFR et supporter une meilleure accessibilite

Update app/components/dsfr/input_component/input_component.html.haml

Co-authored-by: Colin Darie <colin@darie.eu>
This commit is contained in:
Martin 2022-12-20 17:51:36 +01:00 committed by mfo
parent be5b8c2683
commit a4d6692bc6
49 changed files with 314 additions and 439 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 19 KiB

View file

@ -21,10 +21,6 @@
padding-top: $default-spacer; padding-top: $default-spacer;
} }
h1 {
margin-bottom: $default-spacer;
}
.form label { .form label {
margin-bottom: $default-spacer / 2; margin-bottom: $default-spacer / 2;
} }

View file

@ -1,53 +0,0 @@
@import "colors";
@import "constants";
.france-connect-login {
h2 {
color: $black;
}
}
a.france-connect-login-button {
display: inline-block;
height: 60px;
width: 230px;
margin: auto;
margin-bottom: 8px;
background-image: image-url("franceconnect-btn.svg"), image-url("franceconnect-btn-hover.svg");
background-repeat: no-repeat;
background-size: cover;
cursor: pointer;
font-size: 0;
&:hover {
background-image: image-url("franceconnect-btn-hover.svg");
}
}
.france-connect-login-separator {
display: flex;
flex-basis: 100%;
align-items: center;
color: $black;
text-transform: uppercase;
padding-bottom: $default-spacer;
padding-top: $default-spacer;
&::before,
&::after {
content: "";
flex-grow: 1;
background: $dark-grey;
height: 1px;
font-size: 0;
line-height: 0;
}
&::before {
margin-right: $default-spacer;
}
&::after {
margin-left: $default-spacer;
}
}

View file

@ -1,24 +0,0 @@
@import "colors";
@import "constants";
.suspect-email {
background-color: $orange-bg;
text-align: center;
margin-top: -$default-padding * 2;
margin-bottom: $default-padding * 2;
padding: ($default-padding - 2) $default-padding $default-padding $default-padding;
border-radius: 0 0 5px 5px;
}
.email-suggestion-address {
font-weight: bold;
}
.email-suggestion-title {
margin-bottom: $default-spacer;
}
.email-suggestion-answer button {
margin: 0 $default-spacer / 2;
min-width: 66px;
}

View file

@ -1,4 +1,11 @@
class Dsfr::InputComponent < ApplicationComponent class Dsfr::InputComponent < ApplicationComponent
delegate :object, to: :@form
delegate :errors, to: :object
# use it to indicate detailed about the inputs, ex: https://www.systeme-de-design.gouv.fr/elements-d-interface/modeles-et-blocs-fonctionnels/demande-de-mot-de-passe
# it uses aria-describedby on input and link it to yielded content
renders_one :describedby
def initialize(form:, attribute:, input_type:, opts: {}, required: true) def initialize(form:, attribute:, input_type:, opts: {}, required: true)
@form = form @form = form
@attribute = attribute @attribute = attribute
@ -7,60 +14,93 @@ class Dsfr::InputComponent < ApplicationComponent
@required = required @required = required
end end
# add invalid class on input when input is invalid
# and and valid on input only if another input is invalid
def input_group_opts
opts = {
class: class_names('fr-input-group': true,
'fr-password': password?,
"fr-input-group--error": errors_on_attribute?,
"fr-input-group--valid": !errors_on_attribute? && errors_on_another_attribute?)
}
if email?
opts[:data] = { controller: 'email-input' }
end
opts
end
def label_opts
{ class: class_names('fr-label': true, 'fr-password__label': password?) }
end
def input_opts def input_opts
@opts[:class] = class_names(map_array_to_hash_with_true(@opts[:class]) @opts[:class] = class_names(map_array_to_hash_with_true(@opts[:class])
.merge('fr-input': true, .merge('fr-password__input': password?,
'fr-input': true,
'fr-mb-0': true, 'fr-mb-0': true,
'fr-input--error': errors_on_attribute?)) 'fr-input--error': errors_on_attribute?))
if errors_on_attribute? if errors_on_attribute? || describedby
@opts = @opts.deep_merge(aria: { @opts = @opts.deep_merge(aria: {
describedby: error_message_id, describedby: error_message_id,
invalid: true invalid: errors_on_attribute?
}) })
end end
if @required if @required
@opts[:required] = true @opts[:required] = true
end end
if email?
@opts = @opts.deep_merge(data: {
action: "blur->email-input#checkEmail",
'email-input-target': 'input'
})
end
@opts @opts
end end
# add invalid class on input when input is invalid # errors helpers
# and and valid on input only if another input is invalid
def input_group_class_names
class_names('fr-input-group': true,
"fr-input-group--error": errors_on_attribute?,
"fr-input-group--valid": !errors_on_attribute? && errors_on_another_attribute?)
end
# tried to inline it within the template, but failed miserably with a double render
def label
label = @form.object.class.human_attribute_name(@attribute)
if @required
label += tag.span(" *", class: 'mandatory')
end
label
end
def errors_on_attribute? def errors_on_attribute?
@form.object.errors.has_key?(attribute_or_rich_body) errors.has_key?(attribute_or_rich_body)
end end
def error_message_id def error_message_id
dom_id(@form.object, @attribute) dom_id(object, @attribute)
end end
def error_messages def error_messages
@form.object.errors.full_messages_for(attribute_or_rich_body) errors.full_messages_for(attribute_or_rich_body)
end
# i18n lookups
def label
object.class.human_attribute_name(@attribute)
end
def hint
I18n.t("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
end
# kind of input helpers
def password?
@input_type == :password_field
end
def email?
@input_type == :email_field
end end
private private
def errors_on_another_attribute? def hint?
!@form.object.errors.empty? I18n.exists?("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
end end
def errors_on_another_attribute?
!errors.empty?
end
# lookup for edge case from `form.rich_text_area`
# rich text uses _rich_#{attribute}, but it is saved on #{attribute}, as well as error messages
def attribute_or_rich_body def attribute_or_rich_body
case @input_type case @input_type
when :rich_text_area when :rich_text_area

View file

@ -0,0 +1,7 @@
---
en:
show_password:
aria_label: "Show password"
label: "Show"
email_suggest:
wanna_say: 'Do you mean to say ?'

View file

@ -0,0 +1,7 @@
---
fr:
show_password:
aria_label: "Afficher le mot de passe"
label: "Afficher"
email_suggest:
wanna_say: 'Voulez-vous dire ?'

View file

@ -1,5 +1,11 @@
%div{ class: input_group_class_names } = content_tag(:div, input_group_opts) do
= @form.label @attribute, label.html_safe, class: "fr-label" = @form.label @attribute, label_opts do
- capture do
= label
- if @required
%span.mandatory  *
- if hint?
%span.fr-hint-text= hint
= @form.send(@input_type, @attribute, input_opts) = @form.send(@input_type, @attribute, input_opts)
@ -12,3 +18,21 @@
- error_messages.map do |error_message| - error_messages.map do |error_message|
%li= error_message %li= error_message
- elsif describedby.present?
= describedby
- if password?
.fr-password__checkbox.fr-checkbox-group.fr-checkbox-group--sm
%input#show_password{ "aria-label" => t('.show_password.aria_label'), type: "checkbox" }/
%label.fr--password__checkbox.fr-label{ for: "show_password" }= t('.show_password.label')
- if email?
.suspect-email.hidden{ data: { "email-input-target": 'ariaRegion'}, aria: { live: 'off' } }
= render Dsfr::AlertComponent.new(title: t('.email_suggest.wanna_say'), state: :info, heading_level: :div) do |c|
- c.body do
%p{ data: { "email-input-target": 'suggestion'} } exemple@gmail.com &nbsp;?
%p
= button_tag type: 'button', class: 'fr-btn fr-btn--sm fr-mr-3w', data: { action: 'click->email-input#accept'} do
= t('utils.yes')
= button_tag type: 'button', class: 'fr-btn fr-btn--sm', data: { action: 'click->email-input#discard'} do
= t('utils.no')

View file

@ -0,0 +1,33 @@
import { suggest } from 'email-butler';
import { show, hide } from '@utils';
import { ApplicationController } from './application_controller';
export class EmailInputController extends ApplicationController {
static targets = ['ariaRegion', 'suggestion', 'input'];
declare readonly ariaRegionTarget: HTMLElement;
declare readonly suggestionTarget: HTMLElement;
declare readonly inputTarget: HTMLInputElement;
checkEmail() {
const suggestion = suggest(this.inputTarget.value);
if (suggestion && suggestion.full) {
this.suggestionTarget.innerHTML = suggestion.full;
show(this.ariaRegionTarget);
this.ariaRegionTarget.setAttribute('aria-live', 'assertive');
}
}
accept() {
this.ariaRegionTarget.setAttribute('aria-live', 'off');
hide(this.ariaRegionTarget);
this.inputTarget.value = this.suggestionTarget.innerHTML;
this.suggestionTarget.innerHTML = '';
}
discard() {
this.ariaRegionTarget.setAttribute('aria-live', 'off');
hide(this.ariaRegionTarget);
this.suggestionTarget.innerHTML = '';
}
}

View file

@ -24,10 +24,6 @@ import {
motivationCancel, motivationCancel,
showImportJustificatif showImportJustificatif
} from '../new_design/state-button'; } from '../new_design/state-button';
import {
acceptEmailSuggestion,
discardEmailSuggestionBox
} from '../new_design/user-sign_up';
import { showFusion, showNewAccount } from '../new_design/fc-fusion'; import { showFusion, showNewAccount } from '../new_design/fc-fusion';
const application = Application.start(); const application = Application.start();
@ -41,9 +37,7 @@ const DS = {
showImportJustificatif, showImportJustificatif,
showFusion, showFusion,
showNewAccount, showNewAccount,
replaceSemicolonByComma, replaceSemicolonByComma
acceptEmailSuggestion,
discardEmailSuggestionBox
}; };
// Start Rails helpers // Start Rails helpers

View file

@ -31,3 +31,4 @@
@import '@gouvfr/dsfr/dist/component/translate/translate.css'; @import '@gouvfr/dsfr/dist/component/translate/translate.css';
@import '@gouvfr/dsfr/dist/component/pagination/pagination.css'; @import '@gouvfr/dsfr/dist/component/pagination/pagination.css';
@import '@gouvfr/dsfr/dist/component/skiplink/skiplink.css'; @import '@gouvfr/dsfr/dist/component/skiplink/skiplink.css';
@import '@gouvfr/dsfr/dist/component/password/password.css';

View file

@ -1,35 +0,0 @@
import { delegate, show, hide } from '@utils';
import { suggest } from 'email-butler';
const userNewEmailSelector = '#new_user input#user_email';
const passwordFieldSelector = '#new_user input#user_password';
const suggestionsSelector = '.suspect-email';
const emailSuggestionSelector = '.suspect-email .email-suggestion-address';
delegate('focusout', userNewEmailSelector, () => {
// When the user leaves the email input during account creation, we check if this account looks correct.
// If not (e.g if its "bidou@gmail.coo" or "john@yahoo.rf"), we attempt to suggest a fix for the invalid email.
const userEmailInput = document.querySelector(userNewEmailSelector);
const suggestedEmailSpan = document.querySelector(emailSuggestionSelector);
const suggestion = suggest(userEmailInput.value);
if (suggestion && suggestion.full && suggestedEmailSpan) {
suggestedEmailSpan.innerHTML = suggestion.full;
show(document.querySelector(suggestionsSelector));
} else {
hide(document.querySelector(suggestionsSelector));
}
});
export function acceptEmailSuggestion() {
const userEmailInput = document.querySelector(userNewEmailSelector);
const suggestedEmailSpan = document.querySelector(emailSuggestionSelector);
userEmailInput.value = suggestedEmailSpan.innerHTML;
hide(document.querySelector(suggestionsSelector));
document.querySelector(passwordFieldSelector).focus();
}
export function discardEmailSuggestionBox() {
hide(document.querySelector(suggestionsSelector));
}

View file

@ -26,8 +26,7 @@
= link_to t('.whats_agentconnect'), 'https://agentconnect.gouv.fr/', target: '_blank', rel: "noopener" = link_to t('.whats_agentconnect'), 'https://agentconnect.gouv.fr/', target: '_blank', rel: "noopener"
.france-connect-login-separator %p.fr-hr-or= t('views.shared.france_connect_login.separator')
= t('views.shared.france_connect_login.separator')
#session-new.auth-form.sign-in-form #session-new.auth-form.sign-in-form
= form_for User.new, url: user_session_path, html: { class: "form" } do |f| = form_for User.new, url: user_session_path, html: { class: "form" } do |f|

View file

@ -1,6 +1,6 @@
- if FranceConnectService.enabled? - if FranceConnectService.enabled?
.france-connect-login .france-connect-login
%h2.fr-h6.mb-0 %h2.fr-h6
= t('views.shared.france_connect_login.title') = t('views.shared.france_connect_login.title')
%p %p
= t('views.shared.france_connect_login.description') = t('views.shared.france_connect_login.description')
@ -13,7 +13,6 @@
%p %p
= link_to t('views.shared.france_connect_login.help_link'), "https://franceconnect.gouv.fr/", title: new_tab_suffix(t('views.shared.france_connect_login.help_link')), **external_link_attributes = link_to t('views.shared.france_connect_login.help_link'), "https://franceconnect.gouv.fr/", title: new_tab_suffix(t('views.shared.france_connect_login.help_link')), **external_link_attributes
.france-connect-login-separator %p.fr-hr-or= t('views.shared.france_connect_login.separator')
= t('views.shared.france_connect_login.separator')
- else - else
<!-- FranceConnect is not configured --> <!-- FranceConnect is not configured -->

View file

@ -2,26 +2,26 @@
.auth-form .auth-form
= devise_error_messages! = devise_error_messages!
= form_for resource, url: user_registration_path, html: { class: "form" } do |f| = form_for resource, url: user_registration_path, html: { class: "fr-py-5w" } do |f|
%h1.fr-h2= t('views.registrations.new.title', name: APPLICATION_NAME) %h1.fr-h2= t('views.registrations.new.title', name: APPLICATION_NAME)
= render partial: 'shared/france_connect_login', locals: { url: france_connect_particulier_path } = render partial: 'shared/france_connect_login', locals: { url: france_connect_particulier_path }
= f.label :email, t('views.registrations.new.email_label'), id: :user_email_label %fieldset.fr-mb-0.fr-fieldset{ aria: { labelledby: 'create-account-legend' } }
= f.text_field :email, type: :email, autocomplete: 'email', autofocus: true, placeholder: t('views.registrations.new.email_placeholder'), 'aria-describedby': :user_email_label %legend.fr-fieldset__legend#create-account-legend
%h2.fr-h6= I18n.t('views.registrations.new.subtitle')
.suspect-email.hidden .fr-fieldset__element
.email-suggestion-title %p.fr-text--sm= t('utils.asterisk_html')
= t('views.registrations.new.wanna_say')
%span.email-suggestion-address blabla@gmail.com
&nbsp;?
.email-suggestion-answer
= button_tag type: 'button', class: 'button small', onclick: "DS.acceptEmailSuggestion()" do
= t('utils.yes')
= button_tag type: 'button', class: 'button small', onclick: "DS.discardEmailSuggestionBox()" do
= t('utils.no')
= f.label :password, t('views.registrations.new.password_label', min_length: PASSWORD_MIN_LENGTH), id: :user_password_label .fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :email, input_type: :email_field, opts: { autocomplete: 'email', autofocus: true })
= f.password_field :password, autocomplete: 'new-password', value: @user.password, placeholder: t('views.registrations.new.password_placeholder', min_length: PASSWORD_MIN_LENGTH), 'aria-describedby': :user_password_label
= f.submit t('views.shared.account.create'), class: "button large primary expand" .fr-fieldset__element
= render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field, opts: { autocomplete: 'new-password', min_length: PASSWORD_MIN_LENGTH }) do |c|
- c.describedby do
#password-input-messages.fr-messages-group{ "aria-live" => "off" }
%p#password-input-message.fr-message= t('views.registrations.new.password_message')
%p#password-input-message-info.fr-message.fr-message--info= t('views.registrations.new.password_placeholder', min_length: PASSWORD_MIN_LENGTH)
= f.submit t('views.shared.account.create'), class: "fr-btn fr-btn--lg"

View file

@ -1,32 +1,40 @@
= content_for(:page_id, 'auth') = content_for(:page_id, 'auth')
= content_for(:title, t('metas.signin.title')) = content_for(:title, t('metas.signin.title'))
#session-new.auth-form.sign-in-form
= form_for resource, url: user_session_path, html: { class: "form" } do |f| #session-new.auth-form.sign-in-form
%h1.huge-title= t('views.users.sessions.new.sign_in') = form_for resource, url: user_session_path, html: { class: "fr-py-5w" } do |f|
%h1.fr-h2= t('views.users.sessions.new.sign_in', application_name: APPLICATION_NAME)
= render partial: 'shared/france_connect_login', locals: { url: france_connect_particulier_path } = render partial: 'shared/france_connect_login', locals: { url: france_connect_particulier_path }
= f.label :email, t('views.users.sessions.new.email') %fieldset.fr-mb-0.fr-fieldset{ aria: { labelledby: 'new-account-legend' } }
= f.text_field :email, type: :email, autocomplete: 'email', autofocus: true %legend.fr-fieldset__legend#new-account-legend
%h2.fr-h6= I18n.t('views.users.sessions.new.subtitle')
= f.label :password, t('views.users.sessions.new.password', min_length: PASSWORD_MIN_LENGTH) .fr-fieldset__element
= f.password_field :password, autocomplete: 'current-password' %p.fr-text--sm= t('utils.asterisk_html')
.auth-options .fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :email, input_type: :email_field, opts: { autocomplete: 'email', autofocus: true })
.flex-no-shrink
= f.check_box :remember_me .fr-fieldset__element
= f.label :remember_me, t('views.users.sessions.new.remember_me'), class: 'remember-me' = render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field, opts: { autocomplete: 'current-password' })
%p= link_to t('views.users.sessions.new.reset_password'), new_user_password_path, class: "link"
.fr-fieldset__element
.auth-options
.flex-no-shrink
= f.check_box :remember_me
= f.label :remember_me, t('views.users.sessions.new.remember_me'), class: 'remember-me'
.text-right
= link_to t('views.users.sessions.new.reset_password'), new_user_password_path, class: "link"
= f.submit t('views.users.sessions.new.connection'), class: "fr-btn fr-btn--lg" = f.submit t('views.users.sessions.new.connection'), class: "fr-btn fr-btn--lg"
- if AgentConnectService.enabled? - if AgentConnectService.enabled?
.france-connect-login-separator %p.fr-hr-or= t('views.shared.france_connect_login.separator')
= t('views.shared.france_connect_login.separator')
.center .center
%h2.important-header.mb-1= t('views.users.sessions.new.state_civil_servant') %h2.important-header.mb-1= t('views.users.sessions.new.state_civil_servant')
= link_to t('views.users.sessions.new.connect_with_agent_connect'), agent_connect_path, class: "fr-btn fr-btn--secondary" = link_to t('views.users.sessions.new.connect_with_agent_connect'), agent_connect_path, class: "fr-btn fr-btn--secondary"

View file

@ -92,5 +92,11 @@ module TPS
config.view_component.show_previews_source = true config.view_component.show_previews_source = true
config.view_component.default_preview_layout = 'component_preview' config.view_component.default_preview_layout = 'component_preview'
config.view_component.preview_paths << "#{Rails.root}/spec/components/previews" config.view_component.preview_paths << "#{Rails.root}/spec/components/previews"
# rubocop:disable Rails/OutputSafety
config.action_view.field_error_proc = Proc.new do |html_tag, _instance|
html_tag.html_safe # this is generated by rails
end
# rubocop:enable Rails/OutputSafety
end end
end end

View file

@ -97,6 +97,7 @@ search:
## Consider these keys used: ## Consider these keys used:
ignore_unused: ignore_unused:
- 'errors.format'
- 'activerecord.models.*' - 'activerecord.models.*'
- 'activerecord.attributes.*' - 'activerecord.attributes.*'
- 'activerecord.errors.*' - 'activerecord.errors.*'

View file

@ -32,7 +32,6 @@
en: en:
invisible_captcha: invisible_captcha:
sentence_for_humans: 'If you are a human, ignore this field' sentence_for_humans: 'If you are a human, ignore this field'
help: 'Help' help: 'Help'
help_dropdown: help_dropdown:
general_title: "Online help" general_title: "Online help"
@ -141,10 +140,10 @@ en:
registrations: registrations:
new: new:
title: "Create an account %{name}" title: "Create an account %{name}"
subtitle: "Create an account using an email"
email_label: 'Email (name@site.com)' email_label: 'Email (name@site.com)'
email_placeholder: 'Your email address'
wanna_say: 'Do you mean to say'
password_label: "Password (%{min_length} characters minimum)" password_label: "Password (%{min_length} characters minimum)"
password_message: "Your password must have :"
password_placeholder: "%{min_length} characters minimum" password_placeholder: "%{min_length} characters minimum"
invites: invites:
dropdown: dropdown:
@ -314,8 +313,7 @@ en:
actions: "Actions" actions: "Actions"
sessions: sessions:
new: new:
sign_in: Sign in sign_in: Sign in on %{application_name}
email: Email address (name@site.com)
password: Password (minimum length %{min_length} characters) password: Password (minimum length %{min_length} characters)
remember_me: Remember me remember_me: Remember me
reset_password: Forgot password? reset_password: Forgot password?
@ -323,6 +321,7 @@ en:
find_procedure: Find your procedure find_procedure: Find your procedure
state_civil_servant: Are you a state civil servant? state_civil_servant: Are you a state civil servant?
connect_with_agent_connect: Visit our dedicated page connect_with_agent_connect: Visit our dedicated page
subtitle: "Sign in with my account"
passwords: passwords:
reset_link_sent: reset_link_sent:
got_it: Got it! got_it: Got it!
@ -352,10 +351,14 @@ en:
user: user:
one: User one: User
other: Users other: Users
attributes: attributes:
default_attributes: &default_attributes default_attributes: &default_attributes
password: 'password' email: Email
password: 'Password'
requested_merge_into: 'new email address' requested_merge_into: 'new email address'
hints:
email: "Expected format : john.doe@example.com"
user: user:
siret: 'SIRET number' siret: 'SIRET number'
<< : *default_attributes << : *default_attributes
@ -363,8 +366,11 @@ en:
<< : *default_attributes << : *default_attributes
super_admin: super_admin:
<< : *default_attributes << : *default_attributes
instructeur: procedure:
password: 'password' zone: This procedure is run by
champs:
value: Value
errors: errors:
messages: messages:
not_a_phone: 'Invalid phone number' not_a_phone: 'Invalid phone number'
@ -374,35 +380,40 @@ en:
attributes: attributes:
footer: footer:
too_long: ": the footer is too long." too_long: ": the footer is too long."
user:
attributes:
reset_password_token:
# invalid: ": Votre lien de nouveau mot de passe a expiré. Merci den demander un nouveau."
email:
invalid: invalid
taken: already in use
password:
too_short: 'is too short'
password_confirmation:
confirmation: ': The two passwords do not match'
requested_merge_into:
same: "can't be the same as the old one"
invite: invite:
attributes: attributes:
email: email:
taken: ': Invitation already sent' taken: ': Invitation already sent'
user:
attributes: &error_attributes
reset_password_token:
invalid: ": is expired. Ask a new one"
email:
blank: "is empty. Fill in the email"
invalid: "is invalid. Fill in a valid email address, example: john.doe@example.fr"
taken: "already in use. Fill in another email"
password:
too_short: "is too short. Fill in a password with at least 8 characters"
not_strong: "not strong enough. Fill in a stronger password"
password_confirmation:
confirmation: ": The two passwords do not match"
requested_merge_into:
same: "can't be the same as the old one"
instructeur: instructeur:
attributes: attributes:
email: << : *error_attributes
invalid: invalid super_admin:
taken: already in use attributes:
password: << : *error_attributes
too_short: 'is too short'
procedure: procedure:
attributes: attributes:
path: path:
taken: is already used for procedure. You cannot use it because it belongs to another administrator. taken: is already used for procedure. You cannot use it because it belongs to another administrator.
# taken_can_be_claimed: est identique à celui dune autre de vos procedures publiées. Si vous publiez cette procedure, lancienne sera dépubliée et ne sera plus accessible au public. Les utilisateurs qui ont commencé un brouillon vont pouvoir le déposer. taken_can_be_claimed: Is the same as another of your procedure. If you publish this procedure, the other one will be unpublished
invalid: is not valid. It must countain between 3 and 50 characters among a-z, 0-9, '_' and '-'. invalid: is not valid. It must countain between 3 and 50 characters among a-z, 0-9, '_' and '-'.
"champs/cnaf_champ": "champs/cnaf_champ":
attributes: attributes:
@ -417,6 +428,7 @@ en:
reference_avis: reference_avis:
invalid: "must be 13 or 14 characters long" invalid: "must be 13 or 14 characters long"
errors: errors:
format: "Field « %{attribute} » %{message}"
messages: messages:
dossier_not_found: "The file does not exist or you do not have access to it." dossier_not_found: "The file does not exist or you do not have access to it."
# # dossier_map_not_activated: "The file does not have access to the map." # # dossier_map_not_activated: "The file does not have access to the map."

View file

@ -132,11 +132,11 @@ fr:
prefill_link_copy: Copier le lien de préremplissage prefill_link_copy: Copier le lien de préremplissage
registrations: registrations:
new: new:
title: "Créez-vous un compte %{name}" title: "Creation de compte sur %{name}"
subtitle: "Se créer un compte en choisissant un identifiant"
email_label: 'Email' email_label: 'Email'
email_placeholder: 'Votre adresse email'
wanna_say: 'Voulez-vous dire'
password_label: "Mot de passe (%{min_length} caractères minimum)" password_label: "Mot de passe (%{min_length} caractères minimum)"
password_message: "Votre mot de passe doit contenir :"
password_placeholder: "%{min_length} caractères minimum" password_placeholder: "%{min_length} caractères minimum"
invites: invites:
dropdown: dropdown:
@ -310,8 +310,7 @@ fr:
actions: "Actions" actions: "Actions"
sessions: sessions:
new: new:
sign_in: Connectez-vous sign_in: Connexion à %{application_name}
email: Email
password: Mot de passe (%{min_length} caractères minimum) password: Mot de passe (%{min_length} caractères minimum)
remember_me: Se souvenir de moi remember_me: Se souvenir de moi
reset_password: Mot de passe oublié ? reset_password: Mot de passe oublié ?
@ -319,6 +318,7 @@ fr:
find_procedure: Trouvez votre démarche find_procedure: Trouvez votre démarche
state_civil_servant: Vous êtes agent de la fonction publique dÉtat ? state_civil_servant: Vous êtes agent de la fonction publique dÉtat ?
connect_with_agent_connect: Accédez à notre page dédiée connect_with_agent_connect: Accédez à notre page dédiée
subtitle: "Se connecter avec son compte"
passwords: passwords:
reset_link_sent: reset_link_sent:
email_sent_html: "Nous vous avons envoyé un email à ladresse <strong>%{email}</strong>." email_sent_html: "Nous vous avons envoyé un email à ladresse <strong>%{email}</strong>."
@ -349,10 +349,14 @@ fr:
user: user:
one: Utilisateur one: Utilisateur
other: Utilisateurs other: Utilisateurs
attributes: attributes:
default_attributes: &default_attributes default_attributes: &default_attributes
password: 'Le mot de passe' email: Adresse éléctronique
password: 'Mot de passe'
requested_merge_into: 'La nouvelle adresse email' requested_merge_into: 'La nouvelle adresse email'
hints:
email: "Format attendu : john.doe@exemple.fr"
user: user:
siret: 'Numéro SIRET' siret: 'Numéro SIRET'
<< : *default_attributes << : *default_attributes
@ -364,6 +368,7 @@ fr:
zone: La démarche est mise en œuvre par zone: La démarche est mise en œuvre par
champs: champs:
value: Valeur du champ value: Valeur du champ
errors: errors:
messages: messages:
not_a_phone: 'Numéro de téléphone invalide' not_a_phone: 'Numéro de téléphone invalide'
@ -373,35 +378,33 @@ fr:
attributes: attributes:
footer: footer:
too_long: ": le pied de page est trop long." too_long: ": le pied de page est trop long."
user:
attributes:
reset_password_token:
invalid: ": Votre lien de nouveau mot de passe a expiré. Merci den demander un nouveau."
email:
invalid: invalide
taken: déjà utilisé
password: &password
too_short: 'est trop court'
not_strong: 'nest pas assez complexe'
password_confirmation:
confirmation: ': Les deux mots de passe ne correspondent pas'
requested_merge_into:
same: "ne peut être identique à lancienne"
invite: invite:
attributes: attributes:
email: email:
taken: ': Invitation déjà envoyée' taken: ': Invitation déjà envoyée'
user:
attributes: &error_attributes
reset_password_token:
invalid: ": Votre lien de nouveau mot de passe a expiré. Merci den demander un nouveau."
email:
blank: "est vide. Saisir une adresse éléctronique"
invalid: "est invalide. Saisir une adresse éléctronique valide, exemple : john.doe@exemple.fr"
taken: "déjà utilisé. Saisir une autre adresse éléctronique."
password:
too_short: "est trop court. Saisir un mot de passe avec au moins 8 caractères"
not_strong: "nest pas assez complexe. Saisir un mot de passe plus complexe"
password_confirmation:
confirmation: ': Les deux mots de passe ne correspondent pas'
requested_merge_into:
same: "ne peut être identique à lancienne. Saisir une autre adresse email"
instructeur: instructeur:
attributes: attributes:
email: << : *error_attributes
invalid: invalide
taken: déjà utilisé
password:
too_short: 'est trop court'
super_admin: super_admin:
attributes: attributes:
password: << : *error_attributes
<< : *password
procedure: procedure:
attributes: attributes:
path: path:
@ -420,7 +423,9 @@ fr:
invalid: "doit posséder 13 ou 14 caractères" invalid: "doit posséder 13 ou 14 caractères"
reference_avis: reference_avis:
invalid: "doit posséder 13 ou 14 caractères" invalid: "doit posséder 13 ou 14 caractères"
errors: errors:
format: "Le champ « %{attribute} » %{message}"
messages: messages:
saml_not_authorized: "Vous nêtes pas autorisé à accéder à ce service." saml_not_authorized: "Vous nêtes pas autorisé à accéder à ce service."
dossier_not_found: "Le dossier nexiste pas ou vous ny avez pas accès." dossier_not_found: "Le dossier nexiste pas ou vous ny avez pas accès."

View file

@ -10,6 +10,6 @@ fr:
siret: siret:
attributes: attributes:
siret: siret:
length: 'Le numéro SIRET doit comporter 14 chiffres' length: 'est invalide. Saisir un numéro SIRET avec 14 chiffres'
checksum: 'Le numéro SIRET comporte une erreur, vérifiez les chiffres composant le numéro' checksum: 'comporte une erreur. Vérifier les chiffres composant le numéro'
invalid: 'Le numéro SIRET ne correspond pas à un établissement existant' invalid: 'ne correspond pas à un établissement existant'

View file

@ -10,7 +10,7 @@ fr:
reponse_donnee_le: "Réponse donnée le %{date}" reponse_donnee_le: "Réponse donnée le %{date}"
en_attente: "En attente de réponse" en_attente: "En attente de réponse"
france_connect_login: france_connect_login:
title: 'Avec FranceConnect' title: 'Se créer un compte avec FranceConnect'
description: "FranceConnect est la solution proposée par lÉtat pour sécuriser et simplifier la connexion aux services en ligne." description: "FranceConnect est la solution proposée par lÉtat pour sécuriser et simplifier la connexion aux services en ligne."
login_button: "Sidentifier avec" login_button: "Sidentifier avec"
help_link: "Quest-ce que FranceConnect ?" help_link: "Quest-ce que FranceConnect ?"

View file

@ -58,7 +58,7 @@ describe Administrateurs::TypesDeChampController, type: :controller do
it do it do
is_expected.to have_http_status(:ok) is_expected.to have_http_status(:ok)
expect(assigns(:coordinate)).to be_nil expect(assigns(:coordinate)).to be_nil
expect(flash.alert).to eq(["Libelle doit être rempli"]) expect(flash.alert).to eq(["Le champ « Libelle » doit être rempli"])
end end
end end
end end
@ -94,7 +94,7 @@ describe Administrateurs::TypesDeChampController, type: :controller do
it do it do
is_expected.to have_http_status(:ok) is_expected.to have_http_status(:ok)
expect(assigns(:coordinate)).to be_nil expect(assigns(:coordinate)).to be_nil
expect(flash.alert).to eq(["Libelle doit être rempli"]) expect(flash.alert).to eq(["Le champ « Libelle » doit être rempli"])
end end
end end
end end

View file

@ -949,7 +949,7 @@ describe API::V2::GraphqlController do
it "should fail" do it "should fail" do
expect(gql_errors).to eq(nil) expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierEnvoyerMessage: { expect(gql_data).to eq(dossierEnvoyerMessage: {
errors: [{ message: "Votre message ne peut être vide" }], errors: [{ message: "Le champ « Votre message » ne peut être vide" }],
message: nil message: nil
}) })
end end

View file

@ -360,7 +360,7 @@ describe Experts::AvisController, type: :controller do
it do it do
expect(response).to render_template :instruction expect(response).to render_template :instruction
expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"])
expect(Avis.last).to eq(previous_avis) expect(Avis.last).to eq(previous_avis)
expect(dossier.last_avis_updated_at).to eq(nil) expect(dossier.last_avis_updated_at).to eq(nil)
end end
@ -382,7 +382,7 @@ describe Experts::AvisController, type: :controller do
it do it do
expect(response).to render_template :instruction expect(response).to render_template :instruction
expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"])
expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com")
expect(Avis.count).to eq(old_avis_count + 1) expect(Avis.count).to eq(old_avis_count + 1)
end end

View file

@ -608,7 +608,7 @@ describe Instructeurs::DossiersController, type: :controller do
before { subject } before { subject }
it { expect(response).to render_template :avis } it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq(["emaila.com : Email n'est pas valide"]) } it { expect(flash.alert).to eq(["emaila.com : Le champ « Email » n'est pas valide"]) }
it { expect { subject }.not_to change(Avis, :count) } it { expect { subject }.not_to change(Avis, :count) }
it { expect(dossier.last_avis_updated_at).to eq(nil) } it { expect(dossier.last_avis_updated_at).to eq(nil) }
end end
@ -619,7 +619,7 @@ describe Instructeurs::DossiersController, type: :controller do
before { subject } before { subject }
it { expect(response).to render_template :avis } it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq(["toto.fr : Email n'est pas valide"]) } it { expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"]) }
it { expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") } it { expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") }
it { expect(Avis.count).to eq(old_avis_count + 1) } it { expect(Avis.count).to eq(old_avis_count + 1) }
it { expect(saved_avis.expert.email).to eq("titi@titimail.com") } it { expect(saved_avis.expert.email).to eq("titi@titimail.com") }

View file

@ -40,7 +40,7 @@ describe Manager::UsersController, type: :controller do
subject subject
expect(User.find_by(id: user.id).email).not_to eq(nouvel_email) expect(User.find_by(id: user.id).email).not_to eq(nouvel_email)
expect(flash[:error]).to match("Courriel invalide") expect(flash[:error]).to match("Le champ « Adresse éléctronique » est invalide. Saisir une adresse éléctronique valide, exemple : john.doe@exemple.fr")
end end
end end
end end

View file

@ -169,7 +169,7 @@ describe Users::DossiersController, type: :controller do
it do it do
expect(response).not_to have_http_status(:redirect) expect(response).not_to have_http_status(:redirect)
expect(flash[:alert]).to include("Civilité doit être rempli", "Nom doit être rempli", "Prénom doit être rempli") expect(flash[:alert]).to include("Le champ « Civilité » doit être rempli", "Le champ « Nom » doit être rempli", "Le champ « Prénom » doit être rempli")
end end
end end
end end
@ -239,7 +239,7 @@ describe Users::DossiersController, type: :controller do
context 'with an invalid SIRET' do context 'with an invalid SIRET' do
let(:params_siret) { '000 000' } let(:params_siret) { '000 000' }
it_behaves_like 'the request fails with an error', ['Siret Le numéro SIRET doit comporter 14 chiffres'] it_behaves_like 'the request fails with an error', ['Le champ « Siret » est invalide. Saisir un numéro SIRET avec 14 chiffres']
end end
context 'with a valid SIRET' do context 'with a valid SIRET' do

View file

@ -36,7 +36,7 @@ describe Users::ProfilController, type: :controller do
it 'fails' do it 'fails' do
patch :update_email, params: { user: { email: user.email } } patch :update_email, params: { user: { email: user.email } }
expect(response).to have_http_status(302) expect(response).to have_http_status(302)
expect(flash[:alert]).to eq(["La nouvelle adresse email ne peut être identique à lancienne"]) expect(flash[:alert]).to eq(["Le champ « La nouvelle adresse email » ne peut être identique à lancienne. Saisir une autre adresse email"])
end end
end end
@ -83,7 +83,7 @@ describe Users::ProfilController, type: :controller do
end end
it { expect(response).to redirect_to(profil_path) } it { expect(response).to redirect_to(profil_path) }
it { expect(flash.alert).to eq(['Courriel invalide']) } it { expect(flash.alert).to eq(["Le champ « Adresse éléctronique » est invalide. Saisir une adresse éléctronique valide, exemple : john.doe@exemple.fr"]) }
end end
context 'when the user has an instructeur role' do context 'when the user has an instructeur role' do

View file

@ -62,7 +62,7 @@ describe Users::SessionsController, type: :controller do
subject subject
expect(response).to render_template(:new) expect(response).to render_template(:new)
expect(flash.alert).to eq('Courriel ou mot de passe incorrect.') expect(flash.alert).to eq("Adresse éléctronique ou mot de passe incorrect.")
end end
end end
end end

View file

@ -63,7 +63,7 @@ describe Champs::CnafChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Code postal doit posséder 5 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Code postal » doit posséder 5 caractères"])
end end
end end
@ -72,7 +72,7 @@ describe Champs::CnafChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Numero allocataire doit être composé au maximum de 7 chiffres"]) expect(champ.errors.full_messages).to eq(["Le champ « Numero allocataire » doit être composé au maximum de 7 chiffres"])
end end
end end
@ -81,7 +81,7 @@ describe Champs::CnafChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Numero allocataire doit être composé au maximum de 7 chiffres"]) expect(champ.errors.full_messages).to eq(["Le champ « Numero allocataire » doit être composé au maximum de 7 chiffres"])
end end
context 'and the validation_context is :brouillon' do context 'and the validation_context is :brouillon' do
@ -96,7 +96,7 @@ describe Champs::CnafChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Code postal doit posséder 5 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Code postal » doit posséder 5 caractères"])
end end
end end
end end

View file

@ -63,7 +63,7 @@ describe Champs::DgfipChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Reference avis doit posséder 13 ou 14 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Reference avis » doit posséder 13 ou 14 caractères"])
end end
end end
@ -72,7 +72,7 @@ describe Champs::DgfipChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Numero fiscal doit posséder 13 ou 14 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Numero fiscal » doit posséder 13 ou 14 caractères"])
end end
end end
@ -81,7 +81,7 @@ describe Champs::DgfipChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Numero fiscal doit posséder 13 ou 14 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Numero fiscal » doit posséder 13 ou 14 caractères"])
end end
context 'and the validation_context is :brouillon' do context 'and the validation_context is :brouillon' do
@ -96,7 +96,7 @@ describe Champs::DgfipChamp, type: :model do
it do it do
is_expected.to be false is_expected.to be false
expect(champ.errors.full_messages).to eq(["Reference avis doit posséder 13 ou 14 caractères"]) expect(champ.errors.full_messages).to eq(["Le champ « Reference avis » doit posséder 13 ou 14 caractères"])
end end
end end
end end

View file

@ -31,7 +31,7 @@ describe Invite do
it do it do
expect(invite.save).to be false expect(invite.save).to be false
expect(invite.errors.full_messages).to eq(["Email n'est pas valide"]) expect(invite.errors.full_messages).to eq(["Le champ « Email » n'est pas valide"])
end end
context 'when an email is empty' do context 'when an email is empty' do
@ -39,7 +39,7 @@ describe Invite do
it do it do
expect(invite.save).to be false expect(invite.save).to be false
expect(invite.errors.full_messages).to eq(["Email doit être rempli"]) expect(invite.errors.full_messages).to eq(["Le champ « Email » doit être rempli"])
end end
end end
end end

View file

@ -57,7 +57,7 @@ describe ProcedureRevision do
context 'when a libelle is missing' do context 'when a libelle is missing' do
let(:tdc_params) { text_params.except(:libelle) } let(:tdc_params) { text_params.except(:libelle) }
it { expect(subject.errors.full_messages).to eq(["Libelle doit être rempli"]) } it { expect(subject.errors.full_messages).to eq(["Le champ « Libelle » doit être rempli"]) }
end end
context 'when a parent is incorrect' do context 'when a parent is incorrect' do

View file

@ -84,7 +84,7 @@ describe SuperAdmin, type: :model do
let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) } let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) }
it 'reports an error about password length (but not about complexity)' do it 'reports an error about password length (but not about complexity)' do
expect(subject).to eq(["Le mot de passe est trop court"]) expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"])
end end
end end
@ -92,7 +92,7 @@ describe SuperAdmin, type: :model do
context 'when the password is long enough, but too simple' do context 'when the password is long enough, but too simple' do
let(:password) { simple_password } let(:password) { simple_password }
it { expect(subject).to eq(["Le mot de passe nest pas assez complexe"]) } it { expect(subject).to eq(["Le champ « Mot de passe » nest pas assez complexe. Saisir un mot de passe plus complexe"]) }
end end
end end

View file

@ -143,7 +143,7 @@ describe TypeDeChamp do
it { is_expected.to be_invalid } it { is_expected.to be_invalid }
it do it do
subject.validate subject.validate
expect(subject.errors.full_messages.to_sentence).to eq('Troll always invalid') expect(subject.errors.full_messages.to_sentence).to eq("Le champ « Troll » always invalid")
end end
end end
end end
@ -156,13 +156,13 @@ describe TypeDeChamp do
expect(type_de_champ.validate).to be_falsey expect(type_de_champ.validate).to be_falsey
messages = type_de_champ.errors.full_messages messages = type_de_champ.errors.full_messages
expect(messages.size).to eq(1) expect(messages.size).to eq(1)
expect(messages.first.starts_with?("#{type_de_champ.libelle} doit commencer par")).to be_truthy expect(messages.first).to eq("Le champ « #{type_de_champ.libelle} » doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>")
type_de_champ.libelle = '' type_de_champ.libelle = ''
expect(type_de_champ.validate).to be_falsey expect(type_de_champ.validate).to be_falsey
messages = type_de_champ.errors.full_messages messages = type_de_champ.errors.full_messages
expect(messages.size).to eq(2) expect(messages.size).to eq(2)
expect(messages.last.starts_with?("La liste doit commencer par")).to be_truthy expect(messages.last).to eq("Le champ « La liste » doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>")
end end
end end

View file

@ -36,7 +36,7 @@ describe TypesDeChamp::LinkedDropDownListTypeDeChamp do
it { is_expected.to be_invalid } it { is_expected.to be_invalid }
it do it do
subject.validate subject.validate
expect(subject.errors.full_messages).to eq ["#{subject.libelle} doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>"] expect(subject.errors.full_messages).to eq ["Le champ « #{subject.libelle} » doit commencer par une entrée de menu primaire de la forme <code style='white-space: pre-wrap;'>--texte--</code>"]
end end
end end

View file

@ -378,7 +378,7 @@ describe User, type: :model do
let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) } let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) }
it 'reports an error about password length (but not about complexity)' do it 'reports an error about password length (but not about complexity)' do
expect(subject).to eq(["Le mot de passe est trop court"]) expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"])
end end
end end
@ -386,7 +386,7 @@ describe User, type: :model do
context 'when the password is long enough, but too simple' do context 'when the password is long enough, but too simple' do
let(:password) { simple_password } let(:password) { simple_password }
it { expect(subject).to eq(["Le mot de passe nest pas assez complexe"]) } it { expect(subject).to eq(["Le champ « Mot de passe » nest pas assez complexe. Saisir un mot de passe plus complexe"]) }
end end
end end
@ -404,7 +404,7 @@ describe User, type: :model do
let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) } let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) }
it 'reports an error about password length (but not about complexity)' do it 'reports an error about password length (but not about complexity)' do
expect(subject).to eq(["Le mot de passe est trop court"]) expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"])
end end
end end

View file

@ -63,7 +63,7 @@ describe BillSignatureService do
let(:operations_hash) { [['1', 'hash1'], ['2', 'hash3']] } let(:operations_hash) { [['1', 'hash1'], ['2', 'hash3']] }
it do it do
expect { subject }.to raise_error(/La validation a échoué : signature ne correspond pas à lempreinte/) expect { subject }.to raise_error(/La validation a échoué : Le champ « signature » ne correspond pas à lempreinte/)
expect(BillSignature.count).to eq(0) expect(BillSignature.count).to eq(0)
end end
end end

View file

@ -17,6 +17,6 @@ shared_examples "the user has got a prefilled dossier, owned by themselves" do
expect(page).to have_current_path(brouillon_dossier_path(dossier)) 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_text.libelle, with: text_value)
expect(page).to have_field(type_de_champ_phone.libelle, with: phone_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) expect(page).to have_css('label', text: type_de_champ_phone.libelle)
end end
end end

View file

@ -21,7 +21,7 @@ describe 'Creating a new procedure', js: true do
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3' fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
click_on 'Créer la démarche' click_on 'Créer la démarche'
expect(page).to have_text('Libelle doit être rempli') expect(page).to have_text('Le champ « Libelle » doit être rempli')
fill_in_dummy_procedure_details fill_in_dummy_procedure_details
click_on 'Créer la démarche' click_on 'Créer la démarche'

View file

@ -282,7 +282,7 @@ describe 'fetch API Particulier Data', js: true do
wait_until { cnaf_champ.reload.code_postal == 'wrong_code' } wait_until { cnaf_champ.reload.code_postal == 'wrong_code' }
click_on 'Déposer le dossier' click_on 'Déposer le dossier'
expect(page).to have_content(/code postal doit posséder 5 caractères/) expect(page).to have_content(/Le champ « Champs public code postal » doit posséder 5 caractères/)
VCR.use_cassette('api_particulier/success/composition_familiale') do VCR.use_cassette('api_particulier/success/composition_familiale') do
fill_in 'Le code postal', with: code_postal fill_in 'Le code postal', with: code_postal
@ -470,7 +470,7 @@ describe 'fetch API Particulier Data', js: true do
wait_until { dgfip_champ.reload.reference_avis == 'wrong_code' } wait_until { dgfip_champ.reload.reference_avis == 'wrong_code' }
click_on 'Déposer le dossier' click_on 'Déposer le dossier'
expect(page).to have_content(/reference avis doit posséder 13 ou 14 caractères/) expect(page).to have_content(/Le champ « Champs public reference avis » doit posséder 13 ou 14 caractères/)
VCR.use_cassette('api_particulier/success/avis_imposition') do VCR.use_cassette('api_particulier/success/avis_imposition') do
fill_in "La référence davis dimposition", with: reference_avis fill_in "La référence davis dimposition", with: reference_avis

View file

@ -5,7 +5,7 @@ describe 'Accessing the website in different languages:' do
scenario 'I can change the language of the page' do scenario 'I can change the language of the page' do
visit new_user_session_path visit new_user_session_path
expect(page).to have_text('Connectez-vous') expect(page).to have_text("Connexion à #{APPLICATION_NAME}")
find('.fr-translate__btn').click find('.fr-translate__btn').click
find('.fr-nav__link[hreflang="en"]').click find('.fr-nav__link[hreflang="en"]').click

View file

@ -7,8 +7,8 @@ describe 'Signin in:' do
click_on 'Se connecter', match: :first click_on 'Se connecter', match: :first
sign_in_with user.email, 'invalid-password' sign_in_with user.email, 'invalid-password'
expect(page).to have_content 'Courriel ou mot de passe incorrect.' expect(page).to have_content 'Adresse éléctronique ou mot de passe incorrect.'
expect(page).to have_field('Email', with: user.email) expect(page).to have_field('Adresse éléctronique', with: user.email)
sign_in_with user.email, password sign_in_with user.email, password
expect(page).to have_current_path dossiers_path expect(page).to have_current_path dossiers_path

View file

@ -114,7 +114,7 @@ describe 'Creating a new dossier:' do
click_on 'Valider' click_on 'Valider'
expect(page).to have_current_path(siret_dossier_path(dossier)) expect(page).to have_current_path(siret_dossier_path(dossier))
expect(page).to have_content('Le numéro SIRET doit comporter 14 chiffres') expect(page).to have_content('Le champ « Siret » est invalide. Saisir un numéro SIRET avec 14 chiffres')
expect(page).to have_field('Numéro SIRET', with: '0000') expect(page).to have_field('Numéro SIRET', with: '0000')
end end
end end

View file

@ -21,27 +21,27 @@ describe 'Signing up:' do
visit commencer_path(path: procedure.path) visit commencer_path(path: procedure.path)
click_on "Créer un compte #{APPLICATION_NAME}" click_on "Créer un compte #{APPLICATION_NAME}"
expect(page).to have_selector('.suspect-email', visible: false) expect(page).to have_selector('.suspect-email', visible: false)
fill_in 'Email', with: 'bidou@yahoo.rf' fill_in 'Adresse éléctronique', with: 'bidou@yahoo.rf'
fill_in 'Mot de passe', with: '12345' fill_in 'Mot de passe', with: '12345'
end end
scenario 'they can accept the suggestion', js: true do scenario 'they can accept the suggestion', js: true do
expect(page).to have_selector('.suspect-email', visible: true) expect(page).to have_selector('.suspect-email', visible: true)
click_on 'Oui' click_on 'Oui'
expect(page).to have_field("Email", :with => 'bidou@yahoo.fr') expect(page).to have_field("Adresse éléctronique", :with => 'bidou@yahoo.fr')
expect(page).to have_selector('.suspect-email', visible: false) expect(page).to have_selector('.suspect-email', visible: false)
end end
scenario 'they can discard the suggestion', js: true do scenario 'they can discard the suggestion', js: true do
expect(page).to have_selector('.suspect-email', visible: true) expect(page).to have_selector('.suspect-email', visible: true)
click_on 'Non' click_on 'Non'
expect(page).to have_field("Email", :with => 'bidou@yahoo.rf') expect(page).to have_field("Adresse éléctronique", :with => 'bidou@yahoo.rf')
expect(page).to have_selector('.suspect-email', visible: false) expect(page).to have_selector('.suspect-email', visible: false)
end end
scenario 'they can fix the typo themselves', js: true do scenario 'they can fix the typo themselves', js: true do
expect(page).to have_selector('.suspect-email', visible: true) expect(page).to have_selector('.suspect-email', visible: true)
fill_in 'Email', with: 'bidou@yahoo.fr' fill_in 'Adresse éléctronique', with: 'bidou@yahoo.fr'
blur blur
expect(page).to have_selector('.suspect-email', visible: false) expect(page).to have_selector('.suspect-email', visible: false)
end end
@ -54,7 +54,7 @@ describe 'Signing up:' do
expect(page).to have_current_path new_user_registration_path expect(page).to have_current_path new_user_registration_path
sign_up_with user_email, '1234567' sign_up_with user_email, '1234567'
expect(page).to have_current_path user_registration_path expect(page).to have_current_path user_registration_path
expect(page).to have_content 'Le mot de passe est trop court' expect(page).to have_content "Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"
# Then with a good password # Then with a good password
sign_up_with user_email, user_password sign_up_with user_email, user_password

View file

@ -3,7 +3,7 @@ describe 'users/sessions/new.html.haml', type: :view do
before(:each) do before(:each) do
allow(view).to receive(:devise_mapping).and_return(Devise.mappings[:user]) allow(view).to receive(:devise_mapping).and_return(Devise.mappings[:user])
allow(view).to receive(:resource).and_return(:user) allow(view).to receive(:resource).and_return(User.new)
end end
before do before do
@ -12,7 +12,7 @@ describe 'users/sessions/new.html.haml', type: :view do
end end
it 'renders' do it 'renders' do
expect(rendered).to have_field('Email') expect(rendered).to have_field('Adresse éléctronique')
expect(rendered).to have_field('Mot de passe') expect(rendered).to have_field('Mot de passe')
expect(rendered).to have_button('Se connecter') expect(rendered).to have_button('Se connecter')
end end

View file

@ -1,143 +0,0 @@
.btn-fconnect {
all: initial;
color: #0b6ba8;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
background-color: #ffffff;
background-image: none;
border: 1px solid #ccc;
display: inline-block;
margin-bottom: 0;
line-height: 20px;
text-align: center;
text-shadow: 0 1px 1px rgba(255,255,255,0.75);
vertical-align: middle;
cursor: pointer;
border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
}
.btn-fconnect-full {
font-size: 14px;
max-width: 175px;
padding: 11px 19px;
border-radius: 6px;
}
.btn-fconnect-mini {
font-size: 14px;
width: 182px;
padding: 11px 19px;
border-radius: 6px;
}
.btn-fconnect-full img {
width: 100%;
}
.btn-fconnect-mini img {
float:left;
width: 38px;
}
#fconnect-access {
all: initial;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
display: none;
position: absolute;
background: white;
border: 1px solid #ccc;
width: 300px;
padding: 15px;
margin-top: 20px;
z-index: 9990;
box-shadow: 1px 1px 3px #ccc;
}
#fconnect-access hr {
margin: 15px 0;
}
#fconnect-access:after, #fconnect-access:before {
bottom: 100%;
border: solid transparent;
content: "";
position: absolute;
}
#fconnect-access:after {
border-bottom-color: white;
border-width: 13px;
left: 10%;
}
#fconnect-access:before {
border-bottom-color: #ccc;
border-width: 14px;
left: 9.70%;
}
#fconnect-access .logout {
text-align: center;
margin-top: 15px;
}
#fconnect-access .btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
}
#fconnect-access .btn-default {
color: #333;
background-color: #fff;
border-color: #ccc;
}
#fconnect-access .btn-default:hover,
#fconnect-access .btn-default:focus {
color: #333;
background-color: #e6e6e6;
border-color: #adadad;
text-decoration: none;
}
#fc-background {
all: initial;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.80);
position: fixed;
top: 0;
left: 0;
z-index: 9999;
opacity: 0;
transition: opacity 0.2s ease-in;
}
#fc-background.fade-in {
opacity: 1;
}
#fc-background.fade-out {
opacity: 0;
}
#fconnect-iframe {
display: block;
width: 600px;
height: 500px;
margin: 60px auto 0 auto;
}