Skip to content

Commit

Permalink
Fix + refactor formulaire (#225)
Browse files Browse the repository at this point in the history
* 1er refactor (codeCommune en paramètre + rectif de récuperation du cdde postal au lieux de code commune

* import alert dsfr

* renommage composant candidature-validee => candidature-validee-conseiller + rectif wording

* copy candidature validee conseiller en version pour structure

* resolve warning Error: Not implemented: window.scrollTo

* test pour récupérer la valeur après le build (version avant refacto api geo)

* ajout de 2 tests pour le téléphone dans le cas où c'est rempli par l'utilisateur

* refacto codeCommune + api adresse

* correction test

* fix correction required champs

* refactor window scrollTo

* retour commentaire

* refactor code commune + ajout du '*' pur l'input required à true

* fix get info commune
  • Loading branch information
Ornella452 authored Oct 2, 2024
1 parent 65483c0 commit 804a3f3
Show file tree
Hide file tree
Showing 25 changed files with 610 additions and 88 deletions.
6 changes: 4 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ function App() {
const PageCandidatureConseiller = lazy(() => import('./views/candidature-conseiller/PageCandidatureConseiller'));
const PageCandidatureStructure = lazy(() => import('./views/candidature-structure/PageCandidatureStructure'));
const PageCandidatureCoordinateur = lazy(() => import('./views/candidature-coordinateur/PageCandidatureCoordinateur'));
const PageCandidatureValidee = lazy(() => import('./views/candidature-validee/PageCandidatureValidee'));
const PageCandidatureValideeConseiller = lazy(() => import('./views/candidature-validee-conseiller/PageCandidatureValideeConseiller'));
const PageCandidatureValideeStructure = lazy(() => import('./views/candidature-validee-structure/PageCandidatureValideeStructure'));
const PageConfirmationEmailCandidatureConseiller =
lazy(() => import('./views/confirmation-email-candidature-conseiller/PageConfirmationEmailCandidatureConseiller'));
const PageConfirmationEmailCandidatureStructure =
Expand All @@ -54,7 +55,8 @@ function App() {
<Route path="/nouveau-formulaire-conseiller" element={<PageCandidatureConseiller />}/>
<Route path="/nouveau-formulaire-structure" element={<PageCandidatureStructure />}/>
<Route path="/nouveau-formulaire-coordinateur" element={<PageCandidatureCoordinateur />}/>
<Route path="/candidature-validee" element={<PageCandidatureValidee />}/>
<Route path="/candidature-validee-conseiller" element={<PageCandidatureValideeConseiller />}/>
<Route path="/candidature-validee-structure" element={<PageCandidatureValideeStructure />}/>
<Route path="/candidature-confirmer-conseiller/:token" element={<PageConfirmationEmailCandidatureConseiller />}/>
<Route path="/candidature-confirmer-structure/:token" element={<PageConfirmationEmailCandidatureStructure />}/>
<Route path="/kit-communication" element={<KitCommunication />}/>
Expand Down
6 changes: 4 additions & 2 deletions src/components/commun/Input.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function Input({ children, id, isRequired = true, type = 'text', pattern, onChange, list, min, readOnly, isLoading, ariaBusy, value }) {
export default function Input({ children, id, isRequired = true, type = 'text', pattern, onChange, list, min, readOnly, isLoading, ariaBusy, value, testId = '' }) {
return (
<div className="fr-fieldset__element">
<div className="fr-input-group">
Expand All @@ -19,6 +19,7 @@ export default function Input({ children, id, isRequired = true, type = 'text',
name={id}
value={value}
aria-busy={ariaBusy}
data-testid={testId}
/>
{isLoading && (
<div className="fr-input-spinner" aria-hidden="true">
Expand All @@ -42,5 +43,6 @@ Input.propTypes = {
readOnly: PropTypes.bool,
isLoading: PropTypes.bool,
ariaBusy: PropTypes.bool,
value: PropTypes.string
value: PropTypes.string,
testId: PropTypes.string,
};
21 changes: 14 additions & 7 deletions src/views/candidature-conseiller/AddressChooser.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import React from 'react';
import React, { useState } from 'react';
import Input from '../../components/commun/Input';
import { useGeoApi } from './useGeoApi';
import { debounce } from './debounce';

export default function AddressChooser() {
const { searchByName, villes } = useGeoApi();
const [codeCommune, setCodeCommune] = useState('');

return (
<>
<Input
id="lieuHabitation"
list="resultatsRecherche"
isRequired={false}
onChange={debounce(event => searchByName(event.target.value))}
onChange={debounce(async event => {
searchByName(event.target.value);
const codeCommune = await villes.find(({ codesPostaux, nom }) =>
(`${codesPostaux[0]} ${nom}`).toUpperCase() === (event.target.value).toUpperCase())?.code;
setCodeCommune(codeCommune);
})}
>
Votre lieu d’habitation <span className="fr-hint-text">Saississez le nom ou le code postal de votre commune.</span>
Votre lieu d’habitation <span className="cc-obligatoire">*</span>{' '}
<span className="fr-hint-text">Saississez le nom ou le code postal de votre commune.</span>
</Input>
<Input type="hidden" id="lieuHabitationCodeCommune" value={codeCommune} testId="lieuHabitationCodeCommune-hidden"/>
<datalist id="resultatsRecherche">
{villes.map(({ code, nom }) => (
<option value={`${code} ${nom}`} key={code}>
{code} {nom}
{villes.map(({ codesPostaux, nom }, key) => (
<option value={`${codesPostaux[0]} ${nom}`} key={key}>
{codesPostaux[0]} {nom}
</option>
))}
</datalist>
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/CandidatureConseiller.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function CandidatureConseiller() {
setValidationError(error.message);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else {
navigate('/candidature-validee');
navigate('/candidature-validee-conseiller');
}
}
};
Expand Down
230 changes: 216 additions & 14 deletions src/views/candidature-conseiller/CandidatureConseiller.test.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { render, screen, within, fireEvent, act } from '@testing-library/react';
import { render, screen, within, fireEvent, act, renderHook } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import CandidatureConseiller from './CandidatureConseiller';
import { textMatcher, dateDujour } from '../../../test/test-utils';
import * as ReactRouterDom from 'react-router-dom';
import * as useApiAdmin from './useApiAdmin';

vi.mock('react-router-dom', () => ({
useLocation: () => ({ hash: '' }),
Expand Down Expand Up @@ -63,8 +64,11 @@ describe('candidature conseiller', () => {
expect(telephone).toHaveAttribute('type', 'tel');
expect(telephone).toHaveAttribute('pattern', '[+](33|590|596|594|262|269|687)[0-9]{9}');

const habitation = within(etapeInformationsDeContact).getByLabelText('Votre lieu d’habitation Saississez le nom ou le code postal de votre commune.');
const habitation = within(etapeInformationsDeContact).getByLabelText(('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.'));
expect(habitation).toHaveAttribute('type', 'text');

const habitationCodeCommune = within(etapeInformationsDeContact).getByTestId('lieuHabitationCodeCommune-hidden');
expect(habitationCodeCommune).toHaveAttribute('id', 'lieuHabitationCodeCommune');
});

it('quand j’affiche le formulaire alors l’étape "Votre situation et expérience" est affiché', () => {
Expand Down Expand Up @@ -272,28 +276,78 @@ describe('candidature conseiller', () => {
it('quand je renseigne un début de nom de ville qui existe alors plusieurs résultats sont affichés', async () => {
// GIVEN
render(<CandidatureConseiller />);
const geoApiResponse = [
{
code: '75001',
nom: 'Paris',
},
{
code: '82137',
nom: 'Parisot',
const geoApiResponse = {
'type': 'FeatureCollection',
'version': 'draft',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [2.347, 48.859]
},
'properties': {
'label': 'Paris',
'score': 0.730668760330579,
'id': '75056',
'type': 'municipality',
'name': 'Paris',
'postcode': '75001',
'citycode': '75056',
'x': 652089.7,
'y': 6862305.26,
'population': 2133111,
'city': 'Paris',
'context': '75, Paris, Île-de-France',
'importance': 0.67372,
'municipality': 'Paris'
}
},
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [1.869755, 44.253003]
},
'properties': {
'label': 'Parisot',
'score': 0.932836363636364,
'id': '82137',
'banId': '4e195f30-96f0-47c8-82e9-2968b067bccc',
'type': 'municipality',
'name': 'Parisot',
'postcode': '82160',
'citycode': '82137',
'x': 609752.79,
'y': 6351088.82,
'population': 554,
'city': 'Parisot',
'context': '82, Tarn-et-Garonne, Occitanie',
'importance': 0.2612,
'municipality': 'Parisot'
}
}
],
'attribution': 'BAN',
'licence': 'ETALAB-2.0',
'query': 'paris 75002',
'filters': {
'type': 'municipality'
},
];
'limit': 5
};

vi.spyOn(global, 'fetch').mockResolvedValueOnce({
json: async () => Promise.resolve(geoApiResponse)
});

// WHEN
const adresse = screen.getByLabelText('Votre lieu d’habitation Saississez le nom ou le code postal de votre commune.');
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: 'par' } });

// THEN
const paris = await screen.findByRole('option', { name: '75001 Paris', hidden: true });
const parisot = await screen.findByRole('option', { name: '82137 Parisot', hidden: true });
const parisot = await screen.findByRole('option', { name: '82160 Parisot', hidden: true });
expect(paris).toBeInTheDocument();
expect(parisot).toBeInTheDocument();
});
Expand All @@ -317,6 +371,8 @@ describe('candidature conseiller', () => {
fireEvent.change(nom, { target: { value: 'Dupont' } });
const email = screen.getByLabelText('Adresse e-mail * Format attendu : [email protected]');
fireEvent.change(email, { target: { value: '[email protected]' } });
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: '93100 Montreuil' } });
const enEmploi = screen.getByRole('checkbox', { name: 'En emploi' });
fireEvent.click(enEmploi);
const oui = screen.getByRole('radio', { name: 'Oui' });
Expand Down Expand Up @@ -354,6 +410,8 @@ describe('candidature conseiller', () => {
fireEvent.change(nom, { target: { value: 'Dupont' } });
const email = screen.getByLabelText('Adresse e-mail * Format attendu : [email protected]');
fireEvent.change(email, { target: { value: '[email protected]' } });
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: '93100 Montreuil' } });
const oui = screen.getByRole('radio', { name: 'Oui' });
fireEvent.click(oui);
const date = screen.getByLabelText('Choisir une date');
Expand Down Expand Up @@ -389,6 +447,8 @@ describe('candidature conseiller', () => {
fireEvent.change(nom, { target: { value: 'Dupont' } });
const email = screen.getByLabelText('Adresse e-mail * Format attendu : [email protected]');
fireEvent.change(email, { target: { value: '[email protected]' } });
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: '93100 Montreuil' } });
const enEmploi = screen.getByRole('checkbox', { name: 'En emploi' });
fireEvent.click(enEmploi);
const oui = screen.getByRole('radio', { name: 'Oui' });
Expand Down Expand Up @@ -434,6 +494,8 @@ describe('candidature conseiller', () => {
const nom = screen.getByLabelText('Nom *');
fireEvent.change(nom, { target: { value: 'Dupont' } });
const email = screen.getByLabelText('Adresse e-mail * Format attendu : [email protected]');
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: '93100 Montreuil' } });
fireEvent.change(email, { target: { value: '[email protected]' } });
const enEmploi = screen.getByRole('checkbox', { name: 'En emploi' });
fireEvent.click(enEmploi);
Expand All @@ -455,7 +517,147 @@ describe('candidature conseiller', () => {
});

// THEN
expect(mockNavigate).toHaveBeenCalledWith('/candidature-validee');
expect(mockNavigate).toHaveBeenCalledWith('/candidature-validee-conseiller');

vi.useRealTimers();
});

it('quand je remplis complètementle formulaire avec un numéro téléphone valide, alors je suis redirigé vers la page de candidature validée', async () => {
// GIVEN
vi.useFakeTimers();
vi.setSystemTime(new Date(2023, 11, 12, 13));

vi.stubGlobal('fetch', vi.fn(
() => ({ status: 200, json: async () => Promise.resolve({}) }))
);

const mockNavigate = vi.fn().mockReturnValue(() => { });
vi.spyOn(ReactRouterDom, 'useNavigate').mockReturnValue(mockNavigate);

render(<CandidatureConseiller />);
const prenom = screen.getByLabelText('Prénom *');
fireEvent.change(prenom, { target: { value: 'Jean' } });
const nom = screen.getByLabelText('Nom *');
fireEvent.change(nom, { target: { value: 'Dupont' } });
const email = screen.getByLabelText('Adresse e-mail * Format attendu : [email protected]');
fireEvent.change(email, { target: { value: '[email protected]' } });
const adresse = screen.getByLabelText('Votre lieu d’habitation * Saississez le nom ou le code postal de votre commune.');
fireEvent.change(adresse, { target: { value: '93100 Montreuil' } });
const telephone = screen.getByLabelText('Téléphone Format attendu : +33122334455');
fireEvent.change(telephone, { target: { value: '+33159590730' } });
const enEmploi = screen.getByRole('checkbox', { name: 'En emploi' });
fireEvent.click(enEmploi);
const oui = screen.getByRole('radio', { name: 'Oui' });
fireEvent.click(oui);
const date = screen.getByLabelText('Choisir une date');
fireEvent.change(date, { target: { value: dateDujour() } });
const _5km = screen.getByRole('radio', { name: '5 km' });
fireEvent.click(_5km);
const descriptionMotivation = screen.getByLabelText('Votre message *');
fireEvent.change(descriptionMotivation, { target: { value: 'je suis motivé !' } });

// WHEN
const envoyer = screen.getByRole('button', { name: 'Envoyer votre candidature' });

// eslint-disable-next-line testing-library/no-unnecessary-act
await act(() => {
fireEvent.click(envoyer);
});

// THEN
expect(mockNavigate).toHaveBeenCalledWith('/candidature-validee-conseiller');

vi.useRealTimers();
});

it('quand je valide le formulaire alors j’envoie toute les données nescessaires', async () => {
// GIVEN
const formData = [
[
'prenom',
'Jean'
],
[
'nom',
'Dupont'
],
[
'email',
'[email protected]'
],
[
'telephone',
''
],
[
'lieuHabitation',
'93100 Montreuil'
],
[
'lieuHabitationCodeCommune',
'93048'
],
[
'estDemandeurEmploi',
'on'
],
[
'aUneExperienceMedNum',
'oui'
],
[
'dateDisponibilite',
'2023-12-12'
],
[
'distanceMax',
'5'
],
[
'motivation',
'je suis motivé !'
],
[
'g-recaptcha-response',
'1'
],
[
'h-captcha-response',
'1'
]
];
const { buildConseillerData } = renderHook(() => useApiAdmin.useApiAdmin()).result.current;

//WHEN
const result = await buildConseillerData(formData);

// THEN
expect(result).toBe(JSON.stringify({
'prenom': 'Jean',
'nom': 'Dupont',
'email': '[email protected]',
'telephone': '',
'estDemandeurEmploi': true,
'aUneExperienceMedNum': true,
'dateDisponibilite': '2023-12-12',
'distanceMax': '5',
'motivation': 'je suis motivé !',
'h-captcha-response': '1',
'estEnEmploi': false,
'estEnFormation': false,
'estDiplomeMedNum': false,
'nomCommune': 'Montreuil',
'codePostal': '93100',
'codeCommune': '93048',
'location': {
'type': 'Point',
'coordinates': [2.4491,
48.8637]
},
'codeDepartement': '93',
'codeRegion': '11',
'codeCom': null,
}));

vi.useRealTimers();
});
Expand Down
Loading

0 comments on commit 804a3f3

Please sign in to comment.