Complete apartment form validation flow (#46)

This commit is contained in:
nessar committed 2026-08-17 14:18:16 +02:00
1 parent c1e7964f2e
commit d1cdc6eaeb
11 files changed
+351 -21

No files matched your search

@@ -56,6 +56,71 @@ describe('ApartmentForm', () => {
expect(saved).not.toHaveBeenCalled();
});
it('shows every invalid section and focuses the first control in form order', async () => {
const fixture = TestBed.createComponent(ApartmentForm);
fixture.detectChanges();
await fixture.whenStable();
const form = fixture.componentInstance;
const saved = vi.fn();
form.saved.subscribe(saved);
form.apartmentModel.set({
name: ' ',
address: ' ',
purchasePrice: 0,
description: '',
privateAreas: [
{ id: 'area-1', family: 'PIECE_PRINCIPALE', subtype: 'OTHER', label: ' ' },
],
legalProfile: {
...emptyApartmentLegalProfile(),
habitableSurface: -1,
departmentCode: '20',
},
});
form.save();
fixture.detectChanges();
await fixture.whenStable();
const text = fixture.nativeElement.textContent;
expect(saved).not.toHaveBeenCalled();
expect(text).toContain('Le nom de l’appartement est obligatoire.');
expect(text).toContain('Le prix d’acquisition doit être strictement positif.');
expect(text).toContain('L’adresse complète est obligatoire.');
expect(text).toContain('Saisissez un département français');
expect(text).toContain('La surface habitable doit être positive ou nulle.');
expect(text).toContain('Le libellé est obligatoire.');
expect(document.activeElement).toBe(fixture.nativeElement.querySelector('#name'));
});
it('clears its client summary when a field is corrected', async () => {
const fixture = TestBed.createComponent(ApartmentForm);
fixture.detectChanges();
await fixture.whenStable();
const form = fixture.componentInstance;
form.save();
fixture.nativeElement.querySelector('#name').dispatchEvent(new Event('input'));
expect(form.error()).toBe('');
});
it('focuses the first mapped server error after a failed save', async () => {
const fixture = TestBed.createComponent(ApartmentForm);
fixture.detectChanges();
await fixture.whenStable();
fixture.componentRef.setInput('saveFieldErrors', [
{ field: 'address', code: 'not_blank' },
{ field: 'legalProfile.departmentCodeValid', code: 'assert_true' },
{ field: 'privateAreas[0].label', code: 'not_blank' },
]);
fixture.detectChanges();
await fixture.whenStable();
expect(document.activeElement).toBe(fixture.nativeElement.querySelector('#address'));
});
it('rejects whitespace-only fields before the backend does', () => {
const fixture = TestBed.createComponent(ApartmentForm);
const form = fixture.componentInstance;
@@ -198,6 +263,34 @@ describe('ApartmentForm', () => {
);
});
it('keeps malformed private-area server paths in the generic alert', async () => {
const fixture = TestBed.createComponent(ApartmentForm);
fixture.componentRef.setInput('saveError', 'Veuillez corriger les champs signalés.');
fixture.componentRef.setInput('saveFieldErrors', [
{ field: 'privateAreas[].label', code: 'not_blank' },
{ field: 'privateAreas[-1].label', code: 'not_blank' },
]);
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.querySelector('.alert-error').textContent).toContain(
'Veuillez corriger les champs signalés.',
);
});
it('keeps only unknown server validation failures in the generic alert', async () => {
const fixture = TestBed.createComponent(ApartmentForm);
fixture.componentRef.setInput('saveError', 'Veuillez corriger les champs signalés.');
fixture.componentRef.setInput('saveFieldErrors', [{ field: 'name', code: 'not_blank' }]);
fixture.detectChanges();
await fixture.whenStable();
expect(fixture.nativeElement.querySelector('.alert-error')).toBeNull();
expect(fixture.nativeElement.textContent).toContain(
'Le nom de l’appartement est obligatoire.',
);
});
it('emits a trimmed apartment draft when valid', () => {
const fixture = TestBed.createComponent(ApartmentForm);
const form = fixture.componentInstance;