Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix + refactor formulaire #225

Merged
merged 14 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essayer de déplacer ce bout de code dans buildConseillerData et simplifier ce composant

(`${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