Skip to content

Commit

Permalink
feat(alternance): passage recherche en ssr (#2621)
Browse files Browse the repository at this point in the history
* feat(alternance): passage recherche ssr

* fix(jobetudiant): enleve le focus sur le premier element pour permettre le retour au scroll precedent

* refacto(alternance): supprime le service alternance

* refacto(alternance): supprime le controller index alternance

* refacto(alternance): fix test e2e + ajout repository mock

* fix: erreur build

* fix: retour review

* fix: tests

* fix: tests e2e

* test: onSubmit formulaire

* fix: linter
  • Loading branch information
Naorid authored Feb 7, 2024
1 parent 806b280 commit 6d41b12
Show file tree
Hide file tree
Showing 24 changed files with 480 additions and 513 deletions.
2 changes: 2 additions & 0 deletions .env.scalingo
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@ API_IMMERSION_FACILE_STAGE_3EME_API_KEY=${API_IMMERSION_FACILE_STAGE_3EME_API_KE
API_IMMERSION_FACILE_STAGE_3EME_URL=${API_IMMERSION_FACILE_STAGE_3EME_URL}
API_GEO_BASE_URL=${API_GEO_BASE_URL}
API_LA_BONNE_ALTERNANCE_CALLER=${API_LA_BONNE_ALTERNANCE_CALLER}
API_LA_BONNE_ALTERNANCE_IS_ALTERNANCE_MOCK_ACTIVE=${API_LA_BONNE_ALTERNANCE_IS_ALTERNANCE_MOCK_ACTIVE}
API_LA_BONNE_ALTERNANCE_URL=${API_LA_BONNE_ALTERNANCE_URL}
API_LES_ENTREPRISES_SENGAGENT_URL=${API_LES_ENTREPRISES_SENGAGENT_URL}
API_ONISEP_BASE_URL=${API_ONISEP_BASE_URL}
API_ONISEP_ACCOUNT_EMAIL=${API_ONISEP_ACCOUNT_EMAIL}
API_ONISEP_ACCOUNT_PASSWORD=${API_ONISEP_ACCOUNT_PASSWORD}
API_ONISEP_APPLICATION_ID=${API_ONISEP_APPLICATION_ID}
API_POLE_EMPLOI_IS_MOCK_ACTIVE=${API_POLE_EMPLOI_IS_MOCK_ACTIVE}
API_POLE_EMPLOI_OFFRES_URL=${API_POLE_EMPLOI_OFFRES_URL}
API_POLE_EMPLOI_REFERENTIEL_URL=${API_POLE_EMPLOI_REFERENTIEL_URL}
API_TRAJECTOIRES_PRO_URL=${API_TRAJECTOIRES_PRO_URL}
Expand Down
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ POLE_EMPLOI_CONNECT_URL=https://entreprise.pole-emploi.fr

# API LA BONNE ALTERNANCE
API_LA_BONNE_ALTERNANCE_CALLER=1jeune1solution
API_LA_BONNE_ALTERNANCE_IS_ALTERNANCE_MOCK_ACTIVE=1
API_LA_BONNE_ALTERNANCE_URL=https://labonnealternance-recette.apprentissage.beta.gouv.fr/api/
NEXT_PUBLIC_LA_BONNE_ALTERNANCE_URL=https://labonnealternance-recette.apprentissage.beta.gouv.fr/

Expand Down
89 changes: 46 additions & 43 deletions cypress/e2e/apprentissage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@

import { stringify } from 'querystring';

import {
anAlternanceMatcha,
anAlternanceMatchaBoulanger,
anAlternancePEJobs,
} from '~/server/alternances/domain/alternance.fixture';
import { anAlternanceFiltre } from '~/server/alternances/domain/alternance.fixture';
import { aMetier } from '~/server/metiers/domain/metierAlternance.fixture';

import { aMetier } from '../../src/server/metiers/domain/metierAlternance.fixture';
import {
mockedRepositoryReturnsASuccessWhenCodeCommuneIsNot12345,
} from '../../src/server/alternances/infra/repositories/mockAlternance.repository';
import { interceptGet } from '../interceptGet';

const aQuery = {
codeCommune: '13043',
codeRomes: 'D1102, D1104',
distanceCommune: 10,
latitudeCommune: 48.859,
libelleCommune: 'Gignac-la-Nerthe (13180)',
codeCommune: '75056',
codePostal: '75001',
codeRomes: 'D1102,D1104',
distanceCommune: '10',
latitudeCommune: '48.859',
libelleCommune: 'Paris (75001)',
libelleMetier: 'Boulangerie, pâtisserie, chocolaterie',
longitudeCommune: 2.347,
longitudeCommune: '2.347',
ville: 'Paris',
};

describe('Parcours alternance LBA', () => {
Expand All @@ -32,11 +33,6 @@ describe('Parcours alternance LBA', () => {
cy.findByRole('list', { name: /Offres dalternances/i }).should('not.exist');
});

it('place le focus sur le premier input du formulaire de recherche', () => {
cy.visit('/apprentissage');
cy.findByRole('combobox', { name: 'Domaine' }).should('have.focus');
});

describe('Quand l’utilisateur cherche un métier', () => {
it('tous les métiers sont accessibles mais au maximum 10 sont visibles sans scroll', () => {
const listeMetiers = new Array(11).fill(aMetier());
Expand All @@ -57,39 +53,46 @@ describe('Parcours alternance LBA', () => {

describe('Quand l’utilisateur effectue une recherche', () => {
it('affiche les résultats', () => {
const alternances = {
entrepriseList: [],
offreList: [anAlternanceMatcha(), anAlternanceMatchaBoulanger(), anAlternancePEJobs()],
};
interceptGet({
actionBeforeWaitTheCall: () => cy.visit(`/apprentissage?${stringify(aQuery)}`),
alias: 'recherche-metiers',
path: '/api/alternances?*',
response: JSON.stringify(alternances),
const filtre = anAlternanceFiltre({
codeCommune: '75056',
codeRomes: ['D1102', 'D1104'],
distanceCommune: '10',
latitudeCommune: '48.859',
longitudeCommune: '2.347',
});
const query = {
codeCommune: '75056',
codePostal: '75001',
codeRomes: 'D1102,D1104',
distanceCommune: '10',
latitudeCommune: '48.859',
libelleCommune: 'Paris (75001)',
libelleMetier: 'Boulangerie, pâtisserie, chocolaterie',
longitudeCommune: '2.347',
ville: 'Paris',
};
const expectedResult = mockedRepositoryReturnsASuccessWhenCodeCommuneIsNot12345(filtre);

cy.visit(`/apprentissage?${stringify(query)}`);

cy.findByRole('list', { name: /Offres dalternances/i })
.children()
.should('have.length', 3);
.should('have.length', expectedResult?.result.offreList.length);
});
});
});

describe("quand les paramètres de l'url ne respectent pas le schema de validation du controller", () => {
it('retourne une erreur de demande incorrecte', () => {
cy.viewport('iphone-x');
const query = {
...aQuery,
'unwanted-query': 'not-allowed',
};

interceptGet({
actionBeforeWaitTheCall: () => cy.visit(`/apprentissage?${stringify(query)}`),
alias: 'recherche-alternances-failed',
path:'/api/alternances?*',
response: JSON.stringify({ error: "les paramètres dans l'url ne respectent pas le schema de validation" }),
statusCode: 400,
describe('quand la recherche retourne une erreur', () => {
it('affiche l’erreur', () => {
// NOTE (DORO 02-02-2024): Query qui génère une erreur dans le repository mocké
const query = {
...aQuery,
codeCommune: '12345',
};

cy.visit(`/apprentissage?${stringify(query)}`);

cy.findByText(/Service Indisponible/i).should('be.visible');
});
cy.findByText(/Erreur - Demande incorrecte/i).should('be.visible');
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ import { mockUseRouter } from '~/client/components/useRouter.mock';
import { mockSmallScreen } from '~/client/components/window.mock';
import { DependenciesProvider } from '~/client/context/dependenciesContainer.context';
import { aCommuneQuery } from '~/client/hooks/useCommuneQuery';
import { anAlternanceService } from '~/client/services/alternance/alternance.service.fixture';
import { aLocalisationService } from '~/client/services/localisation/localisation.service.fixture';
import { aMetier, aMetierService } from '~/client/services/metiers/metier.fixture';
import { aResultatRechercherMultipleAlternance } from '~/server/alternances/domain/alternance.fixture';
import { createSuccess } from '~/server/errors/either';
import { aListeDeMetierLaBonneAlternance } from '~/server/metiers/domain/metierAlternance.fixture';

Expand All @@ -27,14 +25,12 @@ describe('FormulaireRechercheAlternance', () => {
describe('quand le composant est affiché sans recherche', () => {
it('affiche un formulaire pour la recherche d‘alternance, sans échantillon de résultat', async () => {
// GIVEN
const alternanceService = anAlternanceService();
const localisationService = aLocalisationService();
mockUseRouter({});

// WHEN
render(
<DependenciesProvider
alternanceService={alternanceService}
localisationService={localisationService}
metierLbaService={aMetierService()}
>
Expand All @@ -45,12 +41,10 @@ describe('FormulaireRechercheAlternance', () => {

// THEN
expect(formulaireRechercheAlternance).toBeInTheDocument();
expect(alternanceService.rechercherAlternance).toHaveBeenCalledTimes(0);
});
it('lorsque je séléctionne une commune, affiche le champ rayon', async () => {
render(
<DependenciesProvider
alternanceService={anAlternanceService()}
localisationService={aLocalisationService()}
metierLbaService={aMetierService()}
>
Expand All @@ -70,7 +64,7 @@ describe('FormulaireRechercheAlternance', () => {
});

describe('lorsqu‘on recherche par localisation et par métier', () => {
it('les informations de la localisatione et du métier sont ajoutées à l’url', async () => {
it('les informations de la localisation et du métier sont ajoutées à l’url', async () => {
// Given
const routerPush = jest.fn();
mockUseRouter({ push: routerPush });
Expand All @@ -80,13 +74,11 @@ describe('FormulaireRechercheAlternance', () => {
})];

const localisationService = aLocalisationService();
const alternanceService = anAlternanceService(aResultatRechercherMultipleAlternance().offreList, aResultatRechercherMultipleAlternance().entrepriseList);
const metierService = aMetierService();
jest.spyOn(metierService, 'rechercherMetier').mockResolvedValue(createSuccess(aMetierList));
// When
render(
<DependenciesProvider
alternanceService={alternanceService}
localisationService={localisationService}
metierLbaService={metierService}
>
Expand All @@ -110,15 +102,15 @@ describe('FormulaireRechercheAlternance', () => {
await user.click(submitButton);

// Then
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('libelleMetier=Conduite+de+travaux%2C+direction+de+chantier') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codeRomes=F1201%2CF1202%2CI1101') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('libelleCommune=Paris+%2875006%29') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codeCommune=75056') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('latitudeCommune=48.859') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('longitudeCommune=2.347') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codePostal=75006') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('ville=Paris') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('distanceCommune=10') }, undefined, { shallow: true });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('libelleMetier=Conduite+de+travaux%2C+direction+de+chantier') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codeRomes=F1201%2CF1202%2CI1101') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('libelleCommune=Paris+%2875006%29') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codeCommune=75056') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('latitudeCommune=48.859') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('longitudeCommune=2.347') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('codePostal=75006') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('ville=Paris') }, undefined, { scroll: false });
expect(routerPush).toHaveBeenCalledWith({ query: expect.stringContaining('distanceCommune=10') }, undefined, { scroll: false });
});
});

Expand All @@ -133,13 +125,11 @@ describe('FormulaireRechercheAlternance', () => {
})];

const localisationService = aLocalisationService();
const alternanceService = anAlternanceService(aResultatRechercherMultipleAlternance().offreList, aResultatRechercherMultipleAlternance().entrepriseList);
const metierService = aMetierService();
jest.spyOn(metierService, 'rechercherMetier').mockResolvedValue(createSuccess(aMetierList));
// When
render(
<DependenciesProvider
alternanceService={alternanceService}
localisationService={localisationService}
metierLbaService={metierService}
>
Expand Down Expand Up @@ -172,13 +162,11 @@ describe('FormulaireRechercheAlternance', () => {
})];

const localisationService = aLocalisationService();
const alternanceService = anAlternanceService(aResultatRechercherMultipleAlternance().offreList, aResultatRechercherMultipleAlternance().entrepriseList);
const metierService = aMetierService();
jest.spyOn(metierService, 'rechercherMetier').mockResolvedValue(createSuccess(aMetierList));
// When
render(
<DependenciesProvider
alternanceService={alternanceService}
localisationService={localisationService}
metierLbaService={metierService}
>
Expand Down Expand Up @@ -272,4 +260,40 @@ describe('FormulaireRechercheAlternance', () => {
libelleMetier: expect.anything(),
});
});

describe('lorsqu‘on effectue une recherche', () => {
it('appelle la fonction onSubmit', async () => {
// Given
const localisationService = aLocalisationService();
mockUseRouter({});
const onSubmit = jest.fn();

// When
render(
<DependenciesProvider
localisationService={localisationService}
metierLbaService={aMetierService()}
>
<FormulaireRechercheAlternance onSubmit={onSubmit}/>
</DependenciesProvider>,
);

const user = userEvent.setup();
const inputMetiers = screen.getByRole('combobox', { name: 'Domaine' });
await user.type(inputMetiers, 'boulang');
const firstMetierOption = await screen.findByRole('option', { name: aListeDeMetierLaBonneAlternance()[0].label });
await user.click(firstMetierOption);

const comboboxCommune = screen.getByRole('combobox', { name: 'Localisation' });
await user.type(comboboxCommune, 'Pari');
const localisationOptions = await screen.findAllByRole('option');
await user.click(localisationOptions[0]);

const submitButton = screen.getByRole('button', { name: 'Rechercher' });
await user.click(submitButton);

// Then
expect(onSubmit).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ import { mapToCommune } from '~/client/hooks/useCommuneQuery';
import { MetierService } from '~/client/services/metiers/metier.service';
import { getFormAsQuery } from '~/client/utils/form.util';

export function FormulaireRechercheAlternance() {
function doNothing() {
return;
}

interface FormulaireRechercheAlternanceProps {
onSubmit?: () => void;
}

export function FormulaireRechercheAlternance(props: FormulaireRechercheAlternanceProps) {
const onSubmit = props.onSubmit || doNothing;
const queryParams = useAlternanceQuery();
const {
libelleMetier,
Expand Down Expand Up @@ -49,8 +58,9 @@ export function FormulaireRechercheAlternance() {

async function updateRechercherAlternanceQueryParams(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
onSubmit();
const formEntries = getFormAsQuery(event.currentTarget, queryParams, false);
return router.push({ query: new URLSearchParams(formEntries).toString() }, undefined, { shallow: true });
return router.push({ query: new URLSearchParams(formEntries).toString() }, undefined, { scroll: false });
}

return (
Expand All @@ -67,7 +77,6 @@ export function FormulaireRechercheAlternance() {
<ComboboxMetiers
defaultValue={domaineDefaultValue}
required
autoFocus
placeholder={'Exemples : enseignement, recherche...'}
valueName={'codeRomes'}
/>
Expand Down
Loading

0 comments on commit 6d41b12

Please sign in to comment.