chore: add husky pre-commit hook and lint-staged configuration
- Created a pre-commit hook in .husky to run lint-staged. - Added .lintstagedrc.mjs to format frontend files with Prettier and apply Spotless to backend Java files. - Introduced .prettierignore to exclude specific directories from formatting.
This commit is contained in:
1 parent
5d2d85aa8c
commit
878da155bd
564 files changed
+61467
-47179
No files matched your search
@@ -4,244 +4,244 @@ import { ApartmentForm } from './apartment-form';
|
||||
import { emptyApartmentLegalProfile } from '../../services/apartment.service';
|
||||
|
||||
describe('ApartmentForm', () => {
|
||||
const completeLegalProfile = {
|
||||
taxIdentifier: '751150001234',
|
||||
buildingType: 'APARTMENT_BUILDING' as const,
|
||||
ownershipRegime: 'Copropriété',
|
||||
constructionPeriod: '1990_2005' as const,
|
||||
habitableSurface: 42,
|
||||
equipment: 'Cuisine équipée',
|
||||
heating: 'Individuel électrique',
|
||||
hotWater: 'Ballon électrique',
|
||||
energyRating: 'D' as const,
|
||||
estimatedAnnualEnergyCost: 850,
|
||||
estimatedAnnualEnergyCostReferenceYear: 2021,
|
||||
destination: 'Habitation',
|
||||
privateAccessories: 'Cave',
|
||||
commonAreas: 'Local vélos',
|
||||
internetAccess: 'Fibre',
|
||||
departmentCode: '75',
|
||||
};
|
||||
const completeLegalProfile = {
|
||||
taxIdentifier: '751150001234',
|
||||
buildingType: 'APARTMENT_BUILDING' as const,
|
||||
ownershipRegime: 'Copropriété',
|
||||
constructionPeriod: '1990_2005' as const,
|
||||
habitableSurface: 42,
|
||||
equipment: 'Cuisine équipée',
|
||||
heating: 'Individuel électrique',
|
||||
hotWater: 'Ballon électrique',
|
||||
energyRating: 'D' as const,
|
||||
estimatedAnnualEnergyCost: 850,
|
||||
estimatedAnnualEnergyCostReferenceYear: 2021,
|
||||
destination: 'Habitation',
|
||||
privateAccessories: 'Cave',
|
||||
commonAreas: 'Local vélos',
|
||||
internetAccess: 'Fibre',
|
||||
departmentCode: '75',
|
||||
};
|
||||
|
||||
it('initializes its fields from the apartment being edited', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apt-1',
|
||||
name: 'T2 Paris 15',
|
||||
address: '45 rue des Entrepreneurs, 75015 Paris',
|
||||
purchasePrice: 250000,
|
||||
description: 'Cinquième étage',
|
||||
privateAreas: [],
|
||||
legalProfile: completeLegalProfile,
|
||||
});
|
||||
fixture.detectChanges();
|
||||
it('initializes its fields from the apartment being edited', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apt-1',
|
||||
name: 'T2 Paris 15',
|
||||
address: '45 rue des Entrepreneurs, 75015 Paris',
|
||||
purchasePrice: 250000,
|
||||
description: 'Cinquième étage',
|
||||
privateAreas: [],
|
||||
legalProfile: completeLegalProfile,
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
const form = fixture.componentInstance;
|
||||
expect(form.apartmentModel().name).toBe('T2 Paris 15');
|
||||
expect(form.apartmentModel().purchasePrice).toBe(250000);
|
||||
expect(form.apartmentModel().legalProfile).toEqual(completeLegalProfile);
|
||||
});
|
||||
const form = fixture.componentInstance;
|
||||
expect(form.apartmentModel().name).toBe('T2 Paris 15');
|
||||
expect(form.apartmentModel().purchasePrice).toBe(250000);
|
||||
expect(form.apartmentModel().legalProfile).toEqual(completeLegalProfile);
|
||||
});
|
||||
|
||||
it('shows a validation error instead of emitting an incomplete apartment', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
it('shows a validation error instead of emitting an incomplete apartment', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
|
||||
form.save();
|
||||
form.save();
|
||||
|
||||
expect(form.error()).toContain('Veuillez remplir tous les champs obligatoires');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(form.error()).toContain('Veuillez remplir tous les champs obligatoires');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects whitespace-only fields before the backend does', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: ' ',
|
||||
address: ' ',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: emptyApartmentLegalProfile(),
|
||||
});
|
||||
it('rejects whitespace-only fields before the backend does', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: ' ',
|
||||
address: ' ',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: emptyApartmentLegalProfile(),
|
||||
});
|
||||
|
||||
form.save();
|
||||
form.save();
|
||||
|
||||
expect(form.error()).toContain('Veuillez remplir tous les champs obligatoires');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(form.error()).toContain('Veuillez remplir tous les champs obligatoires');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('emits a trimmed apartment draft when valid', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: ' T2 Paris 15 ',
|
||||
address: ' 45 rue des Entrepreneurs ',
|
||||
purchasePrice: 250000,
|
||||
description: ' Cinquième étage ',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...completeLegalProfile,
|
||||
taxIdentifier: ' 751150001234 ',
|
||||
equipment: ' Cuisine équipée ',
|
||||
departmentCode: ' 2a ',
|
||||
},
|
||||
});
|
||||
it('emits a trimmed apartment draft when valid', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: ' T2 Paris 15 ',
|
||||
address: ' 45 rue des Entrepreneurs ',
|
||||
purchasePrice: 250000,
|
||||
description: ' Cinquième étage ',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...completeLegalProfile,
|
||||
taxIdentifier: ' 751150001234 ',
|
||||
equipment: ' Cuisine équipée ',
|
||||
departmentCode: ' 2a ',
|
||||
},
|
||||
});
|
||||
|
||||
form.save();
|
||||
form.save();
|
||||
|
||||
expect(saved).toHaveBeenCalledWith({
|
||||
id: undefined,
|
||||
name: 'T2 Paris 15',
|
||||
address: '45 rue des Entrepreneurs',
|
||||
purchasePrice: 250000,
|
||||
description: 'Cinquième étage',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...completeLegalProfile,
|
||||
taxIdentifier: '751150001234',
|
||||
equipment: 'Cuisine équipée',
|
||||
departmentCode: '2A',
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(saved).toHaveBeenCalledWith({
|
||||
id: undefined,
|
||||
name: 'T2 Paris 15',
|
||||
address: '45 rue des Entrepreneurs',
|
||||
purchasePrice: 250000,
|
||||
description: 'Cinquième étage',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...completeLegalProfile,
|
||||
taxIdentifier: '751150001234',
|
||||
equipment: 'Cuisine équipée',
|
||||
departmentCode: '2A',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('disables the whole form and preserves a save error while pending', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('saving', true);
|
||||
fixture.componentRef.setInput('saveError', 'Impossible d’enregistrer cet appartement.');
|
||||
fixture.detectChanges();
|
||||
it('disables the whole form and preserves a save error while pending', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('saving', true);
|
||||
fixture.componentRef.setInput('saveError', 'Impossible d’enregistrer cet appartement.');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('fieldset').disabled).toBe(true);
|
||||
expect(fixture.nativeElement.querySelector('[role=status]').closest('fieldset')).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain('Enregistrement…');
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Impossible d’enregistrer cet appartement.',
|
||||
);
|
||||
});
|
||||
expect(fixture.nativeElement.querySelector('fieldset').disabled).toBe(true);
|
||||
expect(fixture.nativeElement.querySelector('[role=status]').closest('fieldset')).toBeNull();
|
||||
expect(fixture.nativeElement.textContent).toContain('Enregistrement…');
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Impossible d’enregistrer cet appartement.',
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves entered values after a save fails', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const form = fixture.componentInstance;
|
||||
form.apartmentModel.set({
|
||||
name: 'T2 rénové',
|
||||
address: '45 rue des Entrepreneurs',
|
||||
purchasePrice: 265000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: emptyApartmentLegalProfile(),
|
||||
});
|
||||
fixture.componentRef.setInput('saveError', 'Impossible d’enregistrer cet appartement.');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
it('preserves entered values after a save fails', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const form = fixture.componentInstance;
|
||||
form.apartmentModel.set({
|
||||
name: 'T2 rénové',
|
||||
address: '45 rue des Entrepreneurs',
|
||||
purchasePrice: 265000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: emptyApartmentLegalProfile(),
|
||||
});
|
||||
fixture.componentRef.setInput('saveError', 'Impossible d’enregistrer cet appartement.');
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('input#name').value).toBe('T2 rénové');
|
||||
expect(fixture.nativeElement.querySelector('input#address').value).toBe(
|
||||
'45 rue des Entrepreneurs',
|
||||
);
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Impossible d’enregistrer cet appartement.',
|
||||
);
|
||||
});
|
||||
expect(fixture.nativeElement.querySelector('input#name').value).toBe('T2 rénové');
|
||||
expect(fixture.nativeElement.querySelector('input#address').value).toBe(
|
||||
'45 rue des Entrepreneurs',
|
||||
);
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Impossible d’enregistrer cet appartement.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects negative legal-profile numbers', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: { ...emptyApartmentLegalProfile(), habitableSurface: -1 },
|
||||
});
|
||||
it('rejects negative legal-profile numbers', () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
const form = fixture.componentInstance;
|
||||
const saved = vi.fn();
|
||||
form.saved.subscribe(saved);
|
||||
form.apartmentModel.set({
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: { ...emptyApartmentLegalProfile(), habitableSurface: -1 },
|
||||
});
|
||||
|
||||
form.save();
|
||||
form.save();
|
||||
|
||||
expect(form.error()).toContain('surface habitable');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(form.error()).toContain('surface habitable');
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('turns an emptied nullable number field back into null', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const surfaceInput: HTMLInputElement =
|
||||
fixture.nativeElement.querySelector('input#habitableSurface');
|
||||
it('turns an emptied nullable number field back into null', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const surfaceInput: HTMLInputElement =
|
||||
fixture.nativeElement.querySelector('input#habitableSurface');
|
||||
|
||||
surfaceInput.value = '42';
|
||||
surfaceInput.dispatchEvent(new Event('input'));
|
||||
surfaceInput.value = '';
|
||||
surfaceInput.dispatchEvent(new Event('input'));
|
||||
await fixture.whenStable();
|
||||
surfaceInput.value = '42';
|
||||
surfaceInput.dispatchEvent(new Event('input'));
|
||||
surfaceInput.value = '';
|
||||
surfaceInput.dispatchEvent(new Event('input'));
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(fixture.componentInstance.apartmentModel().legalProfile.habitableSurface).toBeNull();
|
||||
});
|
||||
expect(fixture.componentInstance.apartmentModel().legalProfile.habitableSurface).toBeNull();
|
||||
});
|
||||
|
||||
it('asks for the energy reference year when an estimated cost is provided', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apartment-1',
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...emptyApartmentLegalProfile(),
|
||||
estimatedAnnualEnergyCost: 850,
|
||||
},
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const saved = vi.fn();
|
||||
fixture.componentInstance.saved.subscribe(saved);
|
||||
it('asks for the energy reference year when an estimated cost is provided', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apartment-1',
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...emptyApartmentLegalProfile(),
|
||||
estimatedAnnualEnergyCost: 850,
|
||||
},
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const saved = vi.fn();
|
||||
fixture.componentInstance.saved.subscribe(saved);
|
||||
|
||||
fixture.nativeElement.querySelector('button[type=submit]').click();
|
||||
fixture.detectChanges();
|
||||
fixture.nativeElement.querySelector('button[type=submit]').click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
"Renseignez l'année de référence du coût énergétique.",
|
||||
);
|
||||
});
|
||||
expect(saved).not.toHaveBeenCalled();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
"Renseignez l'année de référence du coût énergétique.",
|
||||
);
|
||||
});
|
||||
|
||||
it('clears an energy reference year that has no estimated cost', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apartment-1',
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...emptyApartmentLegalProfile(),
|
||||
estimatedAnnualEnergyCostReferenceYear: 2021,
|
||||
},
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const saved = vi.fn();
|
||||
fixture.componentInstance.saved.subscribe(saved);
|
||||
it('clears an energy reference year that has no estimated cost', async () => {
|
||||
const fixture = TestBed.createComponent(ApartmentForm);
|
||||
fixture.componentRef.setInput('apartment', {
|
||||
id: 'apartment-1',
|
||||
name: 'T2',
|
||||
address: '1 rue de Paris',
|
||||
purchasePrice: 250000,
|
||||
description: '',
|
||||
privateAreas: [],
|
||||
legalProfile: {
|
||||
...emptyApartmentLegalProfile(),
|
||||
estimatedAnnualEnergyCostReferenceYear: 2021,
|
||||
},
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const saved = vi.fn();
|
||||
fixture.componentInstance.saved.subscribe(saved);
|
||||
|
||||
fixture.nativeElement.querySelector('button[type=submit]').click();
|
||||
fixture.nativeElement.querySelector('button[type=submit]').click();
|
||||
|
||||
expect(saved).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
legalProfile: expect.objectContaining({
|
||||
estimatedAnnualEnergyCostReferenceYear: null,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
expect(saved).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
legalProfile: expect.objectContaining({
|
||||
estimatedAnnualEnergyCostReferenceYear: null,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user