@open3cl/engine 1.11.1 → 1.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/11_ecs.spec.js +17 -1
- package/13.2_generateur_combustion.spec.js +24 -1
- package/13.2_generateur_combustion_bouilleur.spec.js +8 -0
- package/13.2_generateur_combustion_ch.spec.js +41 -0
- package/14_generateur_ecs.spec.js +72 -0
- package/15_conso_aux.spec.js +20 -0
- package/3.1_b.spec.js +23 -0
- package/3.2.1_mur.spec.js +99 -0
- package/3.2.3_plancher_haut.spec.js +29 -0
- package/3.3_baie_vitree.spec.js +10 -0
- package/3.4_pont_thermique.spec.js +27 -0
- package/4_ventilation.spec.js +12 -0
- package/7_inertie.spec.js +10 -0
- package/9_besoin_ch.spec.js +4 -0
- package/9_conso_ch.js +1 -1
- package/9_emetteur_ch.spec.js +3 -1
- package/9_generateur_ch.spec.js +11 -0
- package/apport_et_besoin.spec.js +5 -1
- package/core/assets/domain/add-additionnal-ue-values-tables.js +0 -19
- package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +3 -4
- package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +25 -0
- package/core/assets/domain/synchronize-valeur-tables.spec.js +43 -0
- package/core/util/logger/log-service.spec.js +11 -0
- package/dpe-sanitizer.service.spec.js +9 -0
- package/engine.js +1 -1
- package/engine.spec.js +43 -3
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +5 -3
- package/package.json +26 -45
- package/utils.js +1 -2
- package/utils.spec.js +16 -0
package/11_ecs.spec.js
CHANGED
|
@@ -13,7 +13,8 @@ vi.mock('./enums.js', () => ({
|
|
|
13
13
|
type_installation: {
|
|
14
14
|
1: 'installation individuelle',
|
|
15
15
|
2: 'installation collective',
|
|
16
|
-
3: 'installation collective multi-bâtiment'
|
|
16
|
+
3: 'installation collective multi-bâtiment',
|
|
17
|
+
4: 'installation de type inconnu'
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
}));
|
|
@@ -566,6 +567,21 @@ describe('calc_ecs - rendement de distribution', () => {
|
|
|
566
567
|
});
|
|
567
568
|
});
|
|
568
569
|
|
|
570
|
+
test('type d’installation ni individuelle ni collective : aucun critère de configuration', () => {
|
|
571
|
+
const de = { enum_type_installation_id: '4' };
|
|
572
|
+
const ecs = makeEcs(de, [
|
|
573
|
+
{
|
|
574
|
+
donnee_entree: { position_volume_chauffe: 1 },
|
|
575
|
+
donnee_intermediaire: { conso_ecs: 0, conso_ecs_depensier: 0 }
|
|
576
|
+
}
|
|
577
|
+
]);
|
|
578
|
+
calc_ecs({}, ecs, 100, 200, 1, 'ca1', 'h1a', 'maison', false, null, null, false);
|
|
579
|
+
|
|
580
|
+
// Le matcher reste vide : ni configuration_logement ni type_reseau_collectif
|
|
581
|
+
expect(tv).toHaveBeenCalledWith('rendement_distribution_ecs', {});
|
|
582
|
+
expect(requestInput).not.toHaveBeenCalled();
|
|
583
|
+
});
|
|
584
|
+
|
|
569
585
|
test('aucune ligne forfaitaire trouvée : rendement de distribution non renseigné', () => {
|
|
570
586
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
571
587
|
vi.mocked(tv).mockReturnValue(undefined);
|
|
@@ -28,7 +28,8 @@ vi.mock('./enums.js', () => ({
|
|
|
28
28
|
type_generateur_ch: {
|
|
29
29
|
89: 'chaudière gaz standard 2001-2015',
|
|
30
30
|
X: 'chaudière fioul',
|
|
31
|
-
Y: 'chaudière gaz'
|
|
31
|
+
Y: 'chaudière gaz',
|
|
32
|
+
Z: 'générateur sans équivalent ECS'
|
|
32
33
|
},
|
|
33
34
|
type_generateur_ecs: {
|
|
34
35
|
50: 'chaudière gaz standard',
|
|
@@ -563,6 +564,28 @@ describe('tv_generateur_combustion - génération mixte (checkEcsVsChauffageForM
|
|
|
563
564
|
errSpy.mockRestore();
|
|
564
565
|
});
|
|
565
566
|
|
|
567
|
+
test('type CH sans équivalent ECS : type de générateur ECS conservé sans message d’alignement', () => {
|
|
568
|
+
state.bug = true;
|
|
569
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
570
|
+
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
571
|
+
const di = { pn: 20000 };
|
|
572
|
+
// CH mixte 'Z' : aucun type ECS portant le même libellé -> pas d'alignement possible
|
|
573
|
+
const de = {
|
|
574
|
+
enum_type_generateur_ecs_id: 'A',
|
|
575
|
+
enum_usage_generateur_id: '3',
|
|
576
|
+
tv_generateur_combustion_id: '42',
|
|
577
|
+
ratio_virtualisation: 1,
|
|
578
|
+
presence_ventouse: 0
|
|
579
|
+
};
|
|
580
|
+
tv_generateur_combustion(dpeAvecChMixte('Z'), di, de, 'ecs', 200, -9, 1);
|
|
581
|
+
expect(tv).toHaveBeenCalledWith(
|
|
582
|
+
'generateur_combustion',
|
|
583
|
+
expect.objectContaining({ enum_type_generateur_ecs_id: 'A' })
|
|
584
|
+
);
|
|
585
|
+
expect(errSpy).not.toHaveBeenCalled();
|
|
586
|
+
errSpy.mockRestore();
|
|
587
|
+
});
|
|
588
|
+
|
|
566
589
|
test('types ECS et CH identiques : type de générateur ECS conservé', () => {
|
|
567
590
|
state.bug = true;
|
|
568
591
|
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
@@ -58,6 +58,14 @@ describe('updateGenerateurBouilleurs - substitution par une chaudière bois équ
|
|
|
58
58
|
expect(de.enum_type_generateur_ch_id).toBe(attendu);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('année numérique antérieure à 1948 : aucune période correspondante, valeur par défaut conservée', () => {
|
|
62
|
+
getFicheTechnique.mockReturnValue({ valeur: '1900' });
|
|
63
|
+
const de = { enum_type_generateur_ch_id: '48' };
|
|
64
|
+
updateGenerateurBouilleurs({}, de, 'ch');
|
|
65
|
+
// Aucun seuil atteint : on garde la chaudière bois par défaut 2004-2012 (values[2004])
|
|
66
|
+
expect(de.enum_type_generateur_ch_id).toBe('58');
|
|
67
|
+
});
|
|
68
|
+
|
|
61
69
|
test('type ECS (granulés 115) : utilise la table ECS des chaudières bois', () => {
|
|
62
70
|
getFicheTechnique.mockReturnValue({ valeur: '2016' });
|
|
63
71
|
const de = { enum_type_generateur_ecs_id: '115' };
|
|
@@ -173,6 +173,27 @@ describe('tv_temp_fonc_30_100 - températures de fonctionnement', () => {
|
|
|
173
173
|
expect(di.temp_fonc_100).toBe(75);
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
+
test('une température à 100 % plus basse issue d’un second émetteur ne remplace pas la valeur retenue', () => {
|
|
177
|
+
tv.mockImplementation((table, matcher) => {
|
|
178
|
+
if (table !== 'temp_fonc_100') return null;
|
|
179
|
+
// Le premier émetteur renvoie 75, le second 60 (plus basse)
|
|
180
|
+
return matcher.enum_temp_distribution_ch_id === '3'
|
|
181
|
+
? { tv_temp_fonc_100_id: 'a', temp_fonc_100: '75' }
|
|
182
|
+
: { tv_temp_fonc_100_id: 'b', temp_fonc_100: '60' };
|
|
183
|
+
});
|
|
184
|
+
const di = {};
|
|
185
|
+
const de = { enum_type_generateur_ch_id: '89' };
|
|
186
|
+
tv_temp_fonc_30_100(
|
|
187
|
+
di,
|
|
188
|
+
de,
|
|
189
|
+
{},
|
|
190
|
+
[emetteur(), emetteur({ enum_temp_distribution_ch_id: '4' })],
|
|
191
|
+
1975
|
|
192
|
+
);
|
|
193
|
+
expect(di.temp_fonc_100).toBe(75);
|
|
194
|
+
expect(de.tv_temp_fonc_100_id).toBe('a');
|
|
195
|
+
});
|
|
196
|
+
|
|
176
197
|
test('aucune ligne forfaitaire trouvée : erreurs signalées pour temp_fonc_30 et temp_fonc_100', () => {
|
|
177
198
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
178
199
|
tv.mockReturnValue(null);
|
|
@@ -709,6 +730,26 @@ describe('calc_generateur_combustion_ch - rendement de génération', () => {
|
|
|
709
730
|
warnSpy.mockRestore();
|
|
710
731
|
});
|
|
711
732
|
|
|
733
|
+
test('bug_for_bug_compat : qp0 déjà exprimé en W (>= 1) conservé sans correction', () => {
|
|
734
|
+
state.bug = true;
|
|
735
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
736
|
+
const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
|
|
737
|
+
const de = {
|
|
738
|
+
enum_type_generateur_ch_id: '89',
|
|
739
|
+
enum_type_energie_id: '2',
|
|
740
|
+
type_energie: 'gaz naturel',
|
|
741
|
+
presence_regulation_combustion: false,
|
|
742
|
+
description: 't'
|
|
743
|
+
};
|
|
744
|
+
calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
|
|
745
|
+
// qp0 >= 1 -> pas de multiplication par 1000, pas d'avertissement
|
|
746
|
+
expect(di.qp0).toBe(200);
|
|
747
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
748
|
+
// même résultat que la correction d'un qp0 de 0,2 kW (référence de régression ci-dessus)
|
|
749
|
+
expect(di.rg).toBeCloseTo(0.8557877790761849, 9);
|
|
750
|
+
warnSpy.mockRestore();
|
|
751
|
+
});
|
|
752
|
+
|
|
712
753
|
test('la présence de régulation modifie le rendement (tf30 vs tf100)', () => {
|
|
713
754
|
const base = () => ({
|
|
714
755
|
pn: 25000,
|
|
@@ -559,6 +559,23 @@ describe('calc_gen_ecs - consommation par générateur', () => {
|
|
|
559
559
|
expect(g.donnee_intermediaire.Qgw).toBe(0);
|
|
560
560
|
});
|
|
561
561
|
|
|
562
|
+
test('installation collective : volume de la fiche non numérique ignoré, volume saisi conservé', () => {
|
|
563
|
+
// « ba » (texte avant le premier « l ») n'est pas un nombre -> VsFiche = NaN, rejeté
|
|
564
|
+
vi.mocked(getFicheTechnique).mockReturnValue({ valeur: 'ballon' });
|
|
565
|
+
ecs_de.enum_type_installation_id = '2';
|
|
566
|
+
ecs_de.ratio_virtualisation = 1;
|
|
567
|
+
const g = gen({
|
|
568
|
+
type_energie: 'gaz',
|
|
569
|
+
enum_type_generateur_ecs_id: 'autre',
|
|
570
|
+
type_stockage_ecs: 'ballon',
|
|
571
|
+
volume_stockage: 150
|
|
572
|
+
});
|
|
573
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'immeuble');
|
|
574
|
+
|
|
575
|
+
// Vs = volume saisi (150), identique au cas d'une fiche « 150 L »
|
|
576
|
+
expect(g.donnee_intermediaire.Qgw).toBeCloseTo(1064620.987190969, 9);
|
|
577
|
+
});
|
|
578
|
+
|
|
562
579
|
test('bug_for_bug_compat : volume proratisé recalé sur la description', () => {
|
|
563
580
|
state.bug = true;
|
|
564
581
|
vi.mocked(getVolumeStockageFromDescription).mockReturnValue(100);
|
|
@@ -852,6 +869,61 @@ describe('calc_gen_ecs - consommation par générateur', () => {
|
|
|
852
869
|
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
853
870
|
});
|
|
854
871
|
|
|
872
|
+
test('type 84 combustion : ligne combustion sans type ECS, type 84 conservé', () => {
|
|
873
|
+
vi.mocked(getChaudiereFioulDefautId).mockReturnValue(undefined);
|
|
874
|
+
vi.mocked(tv).mockReturnValue({ tv_generateur_combustion_id: '42' });
|
|
875
|
+
const g = gen({
|
|
876
|
+
type_energie: 'gaz',
|
|
877
|
+
enum_type_generateur_ecs_id: '84',
|
|
878
|
+
tv_generateur_combustion_id: '42',
|
|
879
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
880
|
+
});
|
|
881
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
882
|
+
|
|
883
|
+
// Aucune liste de types ECS exploitable dans la ligne : pas de redressement
|
|
884
|
+
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
test('type 84 sans combustion ni pompe à chaleur : ni calcul SCOP ni redressement du type', () => {
|
|
888
|
+
const g = gen({
|
|
889
|
+
type_energie: 'gaz',
|
|
890
|
+
enum_type_generateur_ecs_id: '84',
|
|
891
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
892
|
+
});
|
|
893
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
894
|
+
|
|
895
|
+
expect(scopOrCop).not.toHaveBeenCalled();
|
|
896
|
+
expect(tv_generateur_combustion).not.toHaveBeenCalled();
|
|
897
|
+
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
test('bug_for_bug_compat : qp0 déjà en W (>= 1) conservé sans correction', () => {
|
|
901
|
+
state.bug = true;
|
|
902
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
903
|
+
vi.mocked(tvColumnIDs).mockImplementation((table) =>
|
|
904
|
+
table === 'generateur_combustion' ? ['chaud'] : []
|
|
905
|
+
);
|
|
906
|
+
vi.mocked(tv_generateur_combustion).mockImplementation((dpe, di) => {
|
|
907
|
+
di.rpn = 0.9;
|
|
908
|
+
di.qp0 = 100;
|
|
909
|
+
di.pn = 20000;
|
|
910
|
+
di.pveilleuse = 1;
|
|
911
|
+
di.pveil = 0;
|
|
912
|
+
});
|
|
913
|
+
const g = gen({
|
|
914
|
+
type_energie: 'gaz',
|
|
915
|
+
enum_type_generateur_ecs_id: 'chaud',
|
|
916
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
917
|
+
});
|
|
918
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
919
|
+
|
|
920
|
+
expect(g.donnee_intermediaire.qp0).toBe(100);
|
|
921
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
922
|
+
// même rendement que pour un qp0 de 0,1 kW corrigé en 100 W (cf. test de correction)
|
|
923
|
+
expect(g.donnee_intermediaire.rendement_generation).toBeCloseTo(0.3446955189582535, 9);
|
|
924
|
+
warnSpy.mockRestore();
|
|
925
|
+
});
|
|
926
|
+
|
|
855
927
|
test('type 84 pompe à chaleur : déduit via tv_scop_id', () => {
|
|
856
928
|
vi.mocked(scopOrCop).mockImplementation((di) => {
|
|
857
929
|
di.scop = 3;
|
package/15_conso_aux.spec.js
CHANGED
|
@@ -355,6 +355,26 @@ describe('conso_aux_distribution_ch - auxiliaires de distribution', () => {
|
|
|
355
355
|
expect(di.conso_auxiliaire_distribution_ch).toBeCloseTo(372.082418731648, 9);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
+
test('émetteur hors listes (type 1, « autre ») : perte de charge par défaut deltaPem=35, Fcot=0,802', () => {
|
|
359
|
+
const conso = (type) => {
|
|
360
|
+
const em = [
|
|
361
|
+
{
|
|
362
|
+
donnee_entree: {
|
|
363
|
+
enum_type_emission_distribution_id: type,
|
|
364
|
+
enum_temp_distribution_ch_id: '3'
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
];
|
|
368
|
+
const di = {};
|
|
369
|
+
conso_aux_distribution_ch(em, {}, di, {}, 100, 1, 1, '0', 100000);
|
|
370
|
+
return di.conso_auxiliaire_distribution_ch;
|
|
371
|
+
};
|
|
372
|
+
// Type 24 (monotube) : deltaPem=30, même Fcot (0,802) -> seul deltaPem diffère
|
|
373
|
+
expect(conso('1')).toBeGreaterThan(conso('24'));
|
|
374
|
+
// Type 10 (radiateurs autres) : deltaPem=10
|
|
375
|
+
expect(conso('24')).toBeGreaterThan(conso('10'));
|
|
376
|
+
});
|
|
377
|
+
|
|
358
378
|
/**
|
|
359
379
|
* Seuls les émetteurs reliés au générateur (même enum_lien_generateur_emetteur_id) dimensionnent
|
|
360
380
|
* son circulateur. Autotest CSTB IC5-0-3 : chaudière (lien 1) sur plancher chauffant + convecteurs
|
package/3.1_b.spec.js
CHANGED
|
@@ -365,4 +365,27 @@ describe('b - calcul du coefficient de réduction des déperditions', () => {
|
|
|
365
365
|
});
|
|
366
366
|
expect(di.b).toBe(0.7);
|
|
367
367
|
});
|
|
368
|
+
|
|
369
|
+
test('adjacence hors liste aiu/aue (type 10 absent, type inconnu) : seul le type est apparié', () => {
|
|
370
|
+
tv.mockReturnValue({ b: '0.5', tv_coef_reduction_deperdition_id: '7' });
|
|
371
|
+
const di = {};
|
|
372
|
+
const de = { enum_type_adjacence_id: '1' };
|
|
373
|
+
b(di, de, {}, 1);
|
|
374
|
+
expect(tv).toHaveBeenCalledWith(TABLE, { enum_type_adjacence_id: '1' });
|
|
375
|
+
expect(di.b).toBe(0.5);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test('rapport inférieur à la plus petite borne : seule la borne max est renseignée', () => {
|
|
379
|
+
tv.mockReturnValue({ b: '0.9', tv_coef_reduction_deperdition_id: '3' });
|
|
380
|
+
const de = {
|
|
381
|
+
enum_type_adjacence_id: '8',
|
|
382
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
383
|
+
surface_aiu: 1,
|
|
384
|
+
surface_aue: 10
|
|
385
|
+
};
|
|
386
|
+
b({}, de, {}, 1);
|
|
387
|
+
const matcher = tv.mock.calls[0][1];
|
|
388
|
+
expect(matcher.aiu_aue_max).toBe('≤ 0,25');
|
|
389
|
+
expect(matcher.aiu_aue_min).toBeUndefined();
|
|
390
|
+
});
|
|
368
391
|
});
|
package/3.2.1_mur.spec.js
CHANGED
|
@@ -226,6 +226,63 @@ describe('calc_mur - calcul de Umur0 par table forfaitaire (tv_umur0)', () => {
|
|
|
226
226
|
error.mockRestore();
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
test('bug_for_bug : épaisseur absente et ligne tv_umur0_id sans épaisseur : matcher inchangé', () => {
|
|
230
|
+
bugForBugCompat = true;
|
|
231
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
232
|
+
tv.mockImplementation((table, matcher) => {
|
|
233
|
+
if (table === 'umur0' && matcher.tv_umur0_id) return { tv_umur0_id: '1' };
|
|
234
|
+
if (table === 'umur0') return { ...ROW_UMUR0 };
|
|
235
|
+
return null;
|
|
236
|
+
});
|
|
237
|
+
const mur = {
|
|
238
|
+
donnee_entree: {
|
|
239
|
+
methode_saisie_u: 'non isolé',
|
|
240
|
+
methode_saisie_u0:
|
|
241
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
242
|
+
enum_materiaux_structure_mur_id: '5',
|
|
243
|
+
description: 'mur sans épaisseur',
|
|
244
|
+
tv_umur0_id: 1,
|
|
245
|
+
enum_type_doublage_id: '2'
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
250
|
+
|
|
251
|
+
// Aucune épaisseur à reprendre : pas d'avertissement et épaisseur nulle conservée
|
|
252
|
+
expect(error).not.toHaveBeenCalled();
|
|
253
|
+
expect(tv).toHaveBeenLastCalledWith('umur0', {
|
|
254
|
+
enum_materiaux_structure_mur_id: '5',
|
|
255
|
+
epaisseur_structure: 0
|
|
256
|
+
});
|
|
257
|
+
error.mockRestore();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('bug_for_bug : ligne tv_umur0_id introuvable : matcher inchangé', () => {
|
|
261
|
+
bugForBugCompat = true;
|
|
262
|
+
tv.mockImplementation((table, matcher) => {
|
|
263
|
+
if (table === 'umur0' && matcher.tv_umur0_id) return undefined;
|
|
264
|
+
if (table === 'umur0') return { ...ROW_UMUR0 };
|
|
265
|
+
return null;
|
|
266
|
+
});
|
|
267
|
+
const mur = {
|
|
268
|
+
donnee_entree: {
|
|
269
|
+
methode_saisie_u: 'non isolé',
|
|
270
|
+
methode_saisie_u0:
|
|
271
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
272
|
+
enum_materiaux_structure_mur_id: '5',
|
|
273
|
+
tv_umur0_id: 1,
|
|
274
|
+
enum_type_doublage_id: '2'
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
279
|
+
|
|
280
|
+
expect(tv).toHaveBeenLastCalledWith('umur0', {
|
|
281
|
+
enum_materiaux_structure_mur_id: '5',
|
|
282
|
+
epaisseur_structure: 0
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
229
286
|
test('bug_for_bug : épaisseur > 80 recalculée en la divisant par 10 (valeur en millimètres)', () => {
|
|
230
287
|
bugForBugCompat = true;
|
|
231
288
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -423,6 +480,48 @@ describe('calc_mur - prise en compte du doublage', () => {
|
|
|
423
480
|
}
|
|
424
481
|
);
|
|
425
482
|
|
|
483
|
+
test('bug_for_bug : doublage de type 1 sans description : type de doublage inchangé', () => {
|
|
484
|
+
bugForBugCompat = true;
|
|
485
|
+
const mur = {
|
|
486
|
+
donnee_entree: {
|
|
487
|
+
methode_saisie_u: 'non isolé',
|
|
488
|
+
methode_saisie_u0:
|
|
489
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
490
|
+
enum_materiaux_structure_mur_id: '5',
|
|
491
|
+
epaisseur_structure: 30,
|
|
492
|
+
enum_type_doublage_id: '1'
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
497
|
+
|
|
498
|
+
// Type 1 (pas de doublage) : Umur0 de la table non modifié
|
|
499
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('bug_for_bug : type de doublage > 2 inconnu (6) : aucune résistance de doublage ajoutée', () => {
|
|
503
|
+
bugForBugCompat = true;
|
|
504
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
505
|
+
const mur = {
|
|
506
|
+
donnee_entree: {
|
|
507
|
+
methode_saisie_u: 'non isolé',
|
|
508
|
+
methode_saisie_u0:
|
|
509
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
510
|
+
enum_materiaux_structure_mur_id: '5',
|
|
511
|
+
epaisseur_structure: 30,
|
|
512
|
+
enum_type_doublage_id: '6'
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
517
|
+
|
|
518
|
+
// Le type 6 est hors énumération type_doublage (1 à 5) : aucune résistance de doublage n'est
|
|
519
|
+
// définie, ni dans le calcul ni dans la vérification de minoration. Comportement actuel figé :
|
|
520
|
+
// Umur0 devient NaN (donnée d'entrée invalide, non protégée par le moteur).
|
|
521
|
+
expect(mur.donnee_intermediaire.umur0).toBeNaN();
|
|
522
|
+
error.mockRestore();
|
|
523
|
+
});
|
|
524
|
+
|
|
426
525
|
test('bug_for_bug : DPE minorant Umur0 à 2,5 avant doublage : avertissement (Umur0 avant)', () => {
|
|
427
526
|
bugForBugCompat = true;
|
|
428
527
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -342,6 +342,35 @@ describe('calc_ph - compatibilité bug for bug (récupération depuis tv_uph_id)
|
|
|
342
342
|
error.mockRestore();
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
test('ligne tv_uph_id introuvable : type_toiture et effet_joule calculés conservés', () => {
|
|
346
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
347
|
+
tv.mockImplementation((table, matcher) => {
|
|
348
|
+
if (table === 'uph0') return { ...ROW_UPH0 };
|
|
349
|
+
if (table === 'uph' && matcher.tv_uph_id) return undefined;
|
|
350
|
+
if (table === 'uph') return { ...ROW_UPH };
|
|
351
|
+
return null;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const ph = {
|
|
355
|
+
donnee_entree: {
|
|
356
|
+
methode_saisie_u: 'isolation inconnue (table forfaitaire)',
|
|
357
|
+
methode_saisie_u0: 'type de paroi inconnu (valeur par défaut)',
|
|
358
|
+
type_adjacence: 'extérieur',
|
|
359
|
+
type_plancher_haut: 'terrasse',
|
|
360
|
+
tv_uph_id: 7,
|
|
361
|
+
description: 'PH test'
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
calc_ph(ph, 'h1a', '1', '0');
|
|
366
|
+
|
|
367
|
+
// Aucune correction issue du DPE : pas d'avertissement, matcher final en 'terrasse'
|
|
368
|
+
expect(error).not.toHaveBeenCalled();
|
|
369
|
+
expect(tv).toHaveBeenCalledWith('uph', expect.objectContaining({ type_toiture: 'terrasse' }));
|
|
370
|
+
expect(ph.donnee_intermediaire.uph).toBe(0.3);
|
|
371
|
+
error.mockRestore();
|
|
372
|
+
});
|
|
373
|
+
|
|
345
374
|
test('effet_joule du DPE différent : la valeur du DPE est conservée dans le matcher', () => {
|
|
346
375
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
347
376
|
tv.mockImplementation((table, matcher) => {
|
package/3.3_baie_vitree.spec.js
CHANGED
|
@@ -234,6 +234,16 @@ describe('tv_uw - coefficient de transmission de la fenêtre', () => {
|
|
|
234
234
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Pas de valeur forfaitaire uw'));
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
test('Ug exactement tabulé sans ligne uw : uw indéfini et erreur émise', () => {
|
|
238
|
+
// delta_ug = 0 (bornes identiques) mais aucune ligne uw pour ce Ug
|
|
239
|
+
rows.uw = null;
|
|
240
|
+
const bv = baseBv();
|
|
241
|
+
calc_bv(bv, 1);
|
|
242
|
+
|
|
243
|
+
expect(bv.donnee_intermediaire.uw).toBeUndefined();
|
|
244
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Pas de valeur forfaitaire uw'));
|
|
245
|
+
});
|
|
246
|
+
|
|
237
247
|
test('interpolation entre deux Ug tabulés (delta_ug ≠ 0)', () => {
|
|
238
248
|
rows.ug = { ug: '3', tv_ug_id: '5' };
|
|
239
249
|
getRange.mockReturnValue([2.8, 4]);
|
|
@@ -810,6 +810,33 @@ describe('calc_pont_thermique - récupération du mur via la baie vitrée (compa
|
|
|
810
810
|
expect(errorSpy).toHaveBeenCalled();
|
|
811
811
|
});
|
|
812
812
|
|
|
813
|
+
test('hors compatibilité bug : la baie vitrée n’est pas consultée pour retrouver le mur', () => {
|
|
814
|
+
state.bug = false;
|
|
815
|
+
tv.mockReturnValue({ k: '0.5', tv_pont_thermique_id: '42' });
|
|
816
|
+
const mur = [
|
|
817
|
+
{
|
|
818
|
+
donnee_entree: { reference: 'M1', enum_type_adjacence_id: '1', type_isolation: 'iti' },
|
|
819
|
+
donnee_utilisateur: {}
|
|
820
|
+
}
|
|
821
|
+
];
|
|
822
|
+
// Même configuration que le cas bug_for_bug : reference_paroi = index du mur (0)
|
|
823
|
+
const bv = [{ donnee_entree: { reference: 'BV1', reference_paroi: '0' } }];
|
|
824
|
+
const pt = pontThermique({
|
|
825
|
+
type_liaison: 'refend / mur',
|
|
826
|
+
reference_1: 'BV1',
|
|
827
|
+
description: 'refend'
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
calc_pont_thermique(pt, '1', logement({ mur, bv }));
|
|
831
|
+
|
|
832
|
+
// Mur non retrouvé via la baie : isolation ITI par défaut, aucun message de récupération
|
|
833
|
+
expect(tv).toHaveBeenCalledWith('pont_thermique', {
|
|
834
|
+
enum_type_liaison_id: '12',
|
|
835
|
+
isolation_mur: '^iti'
|
|
836
|
+
});
|
|
837
|
+
expect(errorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Aucun mur trouvé'));
|
|
838
|
+
});
|
|
839
|
+
|
|
813
840
|
test('reference_paroi ne correspondant pas à l’index du mur : aucun mur retrouvé (isolation ITI par défaut)', () => {
|
|
814
841
|
state.bug = true;
|
|
815
842
|
tv.mockReturnValue({ k: '0.5', tv_pont_thermique_id: '42' });
|
package/4_ventilation.spec.js
CHANGED
|
@@ -146,6 +146,18 @@ describe('calc_ventilation - orchestration', () => {
|
|
|
146
146
|
);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
test('surface ventilée égale à Sh : conservée sans application de clé de répartition', () => {
|
|
150
|
+
const vt = ventilation({
|
|
151
|
+
surface_ventile: 100,
|
|
152
|
+
cle_repartition_ventilation: 2,
|
|
153
|
+
q4pa_conv_saisi: 1.3
|
|
154
|
+
});
|
|
155
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
156
|
+
|
|
157
|
+
// surface_ventile = Sh = 100 (clé ignorée) => Hvent = 0,34 * 1,2 * 100
|
|
158
|
+
expect(vt.donnee_intermediaire.hvent).toBeCloseTo(40.8, 9);
|
|
159
|
+
});
|
|
160
|
+
|
|
149
161
|
test('surface ventilée différente de Sh sans clé de répartition : divisée par 1', () => {
|
|
150
162
|
// Branche `de.cle_repartition_ventilation || 1` : surface_ventile conservée telle quelle
|
|
151
163
|
const vt = ventilation({ surface_ventile: 50, q4pa_conv_saisi: 1.3 });
|
package/7_inertie.spec.js
CHANGED
|
@@ -54,6 +54,16 @@ describe('Inertie unit tests', () => {
|
|
|
54
54
|
}
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
+
test('plancher haut non lourd (type 9) isolé par ITE ou non isolé : inertie 0', () => {
|
|
58
|
+
expect(
|
|
59
|
+
inertie.calculateInertiePhLourd({ enum_type_isolation_id: 2, enum_type_plancher_haut_id: 9 })
|
|
60
|
+
).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('type de plancher haut inconnu avec isolation ITE : considéré lourd, inertie 1', () => {
|
|
64
|
+
expect(inertie.calculateInertiePhLourd({ enum_type_isolation_id: 4 })).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
57
67
|
test.each([
|
|
58
68
|
[1, 1, 0, 2, 0],
|
|
59
69
|
[1, 1, 0, 3, 0],
|
package/9_besoin_ch.spec.js
CHANGED
|
@@ -93,6 +93,10 @@ describe('calc_Fj - fraction des besoins de chauffage couverte par les apports',
|
|
|
93
93
|
expect(lourde).not.toBeCloseTo(legere, 9);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
test('une inertie inconnue ne définit pas alpha : le résultat n’est pas un nombre', () => {
|
|
97
|
+
expect(calc_Fj(100, 2000, 5000, 1000, 'inconnue')).toBeNaN();
|
|
98
|
+
});
|
|
99
|
+
|
|
96
100
|
test('les inerties "très lourde" et "lourde" partagent le même alpha', () => {
|
|
97
101
|
expect(calc_Fj(100, 2000, 5000, 1000, 'très lourde')).toBeCloseTo(
|
|
98
102
|
calc_Fj(100, 2000, 5000, 1000, 'lourde'),
|
package/9_conso_ch.js
CHANGED
package/9_emetteur_ch.spec.js
CHANGED
|
@@ -23,7 +23,9 @@ vi.mock('./utils.js', () => ({
|
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
vi.mock('./core/tv/infrastructure/tvs.store.js', () => ({
|
|
26
|
-
TvsStore: vi.fn(()
|
|
26
|
+
TvsStore: vi.fn(function () {
|
|
27
|
+
return mockTvsStore;
|
|
28
|
+
})
|
|
27
29
|
}));
|
|
28
30
|
|
|
29
31
|
const { rendement_emission, calc_emetteur_ch, isIntermittenceCollectiveSaisie } =
|
package/9_generateur_ch.spec.js
CHANGED
|
@@ -342,6 +342,17 @@ describe('checkForGeneratorType - classification du générateur', () => {
|
|
|
342
342
|
expect(de.enum_type_generateur_ch_id).toBe('119');
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
test('cas 119 par défaut avec ligne de rendement sans type de générateur : aucune réécriture', () => {
|
|
346
|
+
vi.mocked(requestInputID).mockReturnValue('119');
|
|
347
|
+
vi.mocked(tv).mockReturnValue({ enum_type_generateur_ch_id: undefined });
|
|
348
|
+
const de = { tv_rendement_generation_id: 3, enum_type_generateur_ch_id: '119' };
|
|
349
|
+
const du = {};
|
|
350
|
+
checkForGeneratorType({}, de, {}, du);
|
|
351
|
+
expect(de.enum_type_generateur_ch_id).toBe('119');
|
|
352
|
+
expect(du.isPacGenerator).toBe(false);
|
|
353
|
+
expect(du.isCombustionGenerator).toBe(false);
|
|
354
|
+
});
|
|
355
|
+
|
|
345
356
|
/**
|
|
346
357
|
* Chaudière fioul par défaut : chaudière standard datant de la construction du bâtiment.
|
|
347
358
|
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §17.2.1.1
|
package/apport_et_besoin.spec.js
CHANGED
|
@@ -22,7 +22,11 @@ vi.mock('./6.2_surface_sud_equivalente.js', () => ({ calc_sse: vi.fn() }));
|
|
|
22
22
|
vi.mock('./10_besoin_fr.js', () => ({ default: vi.fn() }));
|
|
23
23
|
vi.mock('./11_besoin_ecs.js', () => ({ default: vi.fn() }));
|
|
24
24
|
vi.mock('./6.1_apport_gratuit.js', () => ({ calc_ai: vi.fn(), calc_as: vi.fn() }));
|
|
25
|
-
vi.mock('./11_nadeq.js', () => ({
|
|
25
|
+
vi.mock('./11_nadeq.js', () => ({
|
|
26
|
+
Nadeq: vi.fn(function () {
|
|
27
|
+
return { calculateNadeq };
|
|
28
|
+
})
|
|
29
|
+
}));
|
|
26
30
|
|
|
27
31
|
const { default: calc_apport_et_besoin } = await import('./apport_et_besoin.js');
|
|
28
32
|
const { calc_sse } = await import('./6.2_surface_sud_equivalente.js');
|
|
@@ -4,25 +4,6 @@ import { UPB_ADDITIONAL_VALUES } from '../../tv/infrastructure/assets/additional
|
|
|
4
4
|
* Add additional values for ue
|
|
5
5
|
*/
|
|
6
6
|
export class AddAdditionnalUeValuesTables {
|
|
7
|
-
/**
|
|
8
|
-
* @type {FileStore}
|
|
9
|
-
*/
|
|
10
|
-
#fileStore;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @type {ApplicationConfig}
|
|
14
|
-
*/
|
|
15
|
-
#appConfig;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param fileStore {FileStore}
|
|
19
|
-
* @param appConfig {ApplicationConfig}
|
|
20
|
-
*/
|
|
21
|
-
constructor(fileStore, appConfig) {
|
|
22
|
-
this.#fileStore = fileStore;
|
|
23
|
-
this.#appConfig = appConfig;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
7
|
/**
|
|
27
8
|
* Ajout de valeurs supplémentaires pour le calcul du facteur Ue pour les déperditions plancher_bas
|
|
28
9
|
* @param tableValues
|
|
@@ -3,12 +3,11 @@ import { AddAdditionnalUeValuesTables } from './add-additionnal-ue-values-tables
|
|
|
3
3
|
import { UPB_ADDITIONAL_VALUES } from '../../tv/infrastructure/assets/additional-ue-values.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* dans la table `ue`.
|
|
6
|
+
* Le service n'a aucune dépendance : on vérifie la logique d'aplatissement des
|
|
7
|
+
* valeurs Ue additionnelles dans la table `ue`.
|
|
9
8
|
*/
|
|
10
9
|
describe('AddAdditionnalUeValuesTables', () => {
|
|
11
|
-
const buildService = () => new AddAdditionnalUeValuesTables(
|
|
10
|
+
const buildService = () => new AddAdditionnalUeValuesTables();
|
|
12
11
|
|
|
13
12
|
test('ajoute toutes les valeurs additionnelles dans la table ue', () => {
|
|
14
13
|
const nbAttendu = UPB_ADDITIONAL_VALUES.reduce((acc, entree) => acc + entree.values.length, 0);
|
|
@@ -69,4 +69,29 @@ describe('SynchronizeDpeGesLimitValuesTables unit tests', () => {
|
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
|
+
test('regroupe plusieurs surfaces sous une même classe d’altitude', () => {
|
|
73
|
+
const fileStore = new FileStore();
|
|
74
|
+
const appConfig = new ApplicationConfig();
|
|
75
|
+
const synchronizeDpeGesLimitValuesTables = new SynchronizeDpeGesLimitValuesTables(
|
|
76
|
+
fileStore,
|
|
77
|
+
appConfig
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
vi.spyOn(fileStore, 'readLocalOdsFileAndConvertToJson').mockResolvedValue({
|
|
81
|
+
dpe_class_limit: [
|
|
82
|
+
{ classe_altitude: '400-800m', surface: '10', A: '124' },
|
|
83
|
+
{ classe_altitude: '400-800m', surface: '20', A: '100' }
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
vi.spyOn(ApplicationConfig.prototype, 'solicitationsExtFilePath', 'get').mockReturnValue(
|
|
87
|
+
'src/file.ods'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return synchronizeDpeGesLimitValuesTables.execute().then((output) => {
|
|
91
|
+
// Le groupe d'altitude n'est créé qu'une fois : la 1ʳᵉ surface n'est pas écrasée
|
|
92
|
+
expect(output).toStrictEqual({
|
|
93
|
+
dpe_class_limit: { '400-800m': { 10: { A: 124 }, 20: { A: 100 } } }
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
72
97
|
});
|
|
@@ -99,4 +99,47 @@ describe('SynchronizeValeurTables unit tests', () => {
|
|
|
99
99
|
expect(contenu).toContain('valeurs-ue-completees');
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
test('force isolation_surfaces à "0" uniquement lorsque la valeur est absente dans q4pa_conv', () => {
|
|
103
|
+
const fileStore = new FileStore();
|
|
104
|
+
const appConfig = new ApplicationConfig();
|
|
105
|
+
const synchronizeC1Tables = new SynchronizeC1Tables(fileStore, appConfig);
|
|
106
|
+
const synchronizeSolicitationTables = new SynchronizeSolicitationsTables(fileStore, appConfig);
|
|
107
|
+
const synchronizeDpeGesLimitValuesTables = new SynchronizeDpeGesLimitValuesTables(
|
|
108
|
+
fileStore,
|
|
109
|
+
appConfig
|
|
110
|
+
);
|
|
111
|
+
const addAdditionnalUeValuesTables = new AddAdditionnalUeValuesTables(fileStore, appConfig);
|
|
112
|
+
const synchronizeValeurTables = new SynchronizeValeurTables(
|
|
113
|
+
fileStore,
|
|
114
|
+
appConfig,
|
|
115
|
+
synchronizeSolicitationTables,
|
|
116
|
+
synchronizeDpeGesLimitValuesTables,
|
|
117
|
+
addAdditionnalUeValuesTables,
|
|
118
|
+
synchronizeC1Tables
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
vi.spyOn(ApplicationConfig.prototype, 'ademeValeurTablesFileUrl', 'get').mockReturnValue(
|
|
122
|
+
'http://localhost/file.xlsx'
|
|
123
|
+
);
|
|
124
|
+
vi.spyOn(ApplicationConfig.prototype, 'assetsOutputFolder', 'get').mockReturnValue(
|
|
125
|
+
'src/assets'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Le parsing Excel ignore les valeurs à 0 : la 1ʳᵉ ligne n'a pas de isolation_surfaces,
|
|
129
|
+
// la 2ᵉ en possède déjà une qui doit être conservée.
|
|
130
|
+
vi.spyOn(fileStore, 'downloadXlsxFileAndConvertToJson').mockResolvedValue({
|
|
131
|
+
q4pa_conv: [{ q4pa_conv: 'a' }, { q4pa_conv: 'b', isolation_surfaces: 'oui' }]
|
|
132
|
+
});
|
|
133
|
+
vi.spyOn(fileStore, 'writeFileToLocalSystem').mockResolvedValue(null);
|
|
134
|
+
vi.spyOn(synchronizeC1Tables, 'execute').mockResolvedValue({});
|
|
135
|
+
vi.spyOn(synchronizeSolicitationTables, 'execute').mockResolvedValue({});
|
|
136
|
+
vi.spyOn(synchronizeDpeGesLimitValuesTables, 'execute').mockResolvedValue({});
|
|
137
|
+
vi.spyOn(addAdditionnalUeValuesTables, 'execute').mockResolvedValue({});
|
|
138
|
+
|
|
139
|
+
return synchronizeValeurTables.execute().then(() => {
|
|
140
|
+
const contenu = vi.mocked(fileStore.writeFileToLocalSystem).mock.calls[0][1];
|
|
141
|
+
const tvs = JSON.parse(contenu.match(/tvs = (\{[\s\S]*\});\n export/)[1]);
|
|
142
|
+
expect(tvs.q4pa_conv.map((l) => l.isolation_surfaces)).toEqual(['0', 'oui']);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
102
145
|
});
|
|
@@ -58,6 +58,17 @@ describe('Service de logs (log-service)', () => {
|
|
|
58
58
|
expect(console.error).toBe(consoleOrigine.error);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('setLoggerOn sans option conserve console.error neutralisé', () => {
|
|
62
|
+
setLoggerOff(true);
|
|
63
|
+
const erreurNeutralisee = console.error;
|
|
64
|
+
setLoggerOn();
|
|
65
|
+
|
|
66
|
+
expect(logger.silent).toBe(false);
|
|
67
|
+
expect(console.log).toBe(consoleOrigine.log);
|
|
68
|
+
expect(console.error).toBe(erreurNeutralisee);
|
|
69
|
+
expect(console.error).not.toBe(consoleOrigine.error);
|
|
70
|
+
});
|
|
71
|
+
|
|
61
72
|
test('les méthodes console neutralisées sont des fonctions no-op', () => {
|
|
62
73
|
setLoggerOff();
|
|
63
74
|
|
|
@@ -77,6 +77,15 @@ describe('DpeSanitizerService.execute - collections manquantes', () => {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
describe('DpeSanitizerService - transformation des clés', () => {
|
|
81
|
+
test('conserve les clés inchangées (transformation identité)', () => {
|
|
82
|
+
vi.mocked(has).mockReturnValue(true);
|
|
83
|
+
service.execute({});
|
|
84
|
+
const keyTransform = vi.mocked(ObjectUtil.deepObjectTransform).mock.calls[0][1];
|
|
85
|
+
expect(keyTransform('generateur_ecs')).toBe('generateur_ecs');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
80
89
|
/**
|
|
81
90
|
* Récupère la fonction de transformation des valeurs passée en 3e argument de
|
|
82
91
|
* `deepObjectTransform` après un appel à `execute`.
|
package/engine.js
CHANGED
|
@@ -36,7 +36,7 @@ import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
|
36
36
|
import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
|
|
37
37
|
import { reconstituerRdimEcsIndividuelleUnique } from './11_rdim_ecs_individuelle_unique.js';
|
|
38
38
|
|
|
39
|
-
const LIB_VERSION = '1.11.
|
|
39
|
+
const LIB_VERSION = '1.11.2';
|
|
40
40
|
|
|
41
41
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
42
42
|
|
package/engine.spec.js
CHANGED
|
@@ -83,9 +83,15 @@ vi.mock('./11_rdim_ecs_individuelle_unique.js', () => ({
|
|
|
83
83
|
const calculateInertie = vi.fn();
|
|
84
84
|
const calculateEnr = vi.fn();
|
|
85
85
|
const ratioProrataPvLogement = vi.fn();
|
|
86
|
-
vi.mock('./7_inertie.js', () => ({
|
|
86
|
+
vi.mock('./7_inertie.js', () => ({
|
|
87
|
+
Inertie: vi.fn(function () {
|
|
88
|
+
return { calculateInertie };
|
|
89
|
+
})
|
|
90
|
+
}));
|
|
87
91
|
vi.mock('./16.2_production_enr.js', () => ({
|
|
88
|
-
ProductionENR: vi.fn(()
|
|
92
|
+
ProductionENR: vi.fn(function () {
|
|
93
|
+
return { calculateEnr };
|
|
94
|
+
}),
|
|
89
95
|
ratioProrataPvLogement: (...args) => ratioProrataPvLogement(...args)
|
|
90
96
|
}));
|
|
91
97
|
|
|
@@ -93,7 +99,9 @@ vi.mock('./16.2_production_enr.js', () => ({
|
|
|
93
99
|
// renvoie l'entrée telle quelle.
|
|
94
100
|
const sanitizeExecute = vi.fn((dpe) => dpe);
|
|
95
101
|
vi.mock('./dpe-sanitizer.service.js', () => ({
|
|
96
|
-
default: vi.fn(()
|
|
102
|
+
default: vi.fn(function () {
|
|
103
|
+
return { execute: sanitizeExecute };
|
|
104
|
+
})
|
|
97
105
|
}));
|
|
98
106
|
|
|
99
107
|
const { getVersion, get_conso_coeff_1_7_2027, get_classe_ges_dpe, calcul_3cl, calcul_3cl_xml } =
|
|
@@ -650,6 +658,20 @@ describe('calcul_3cl - collections vidables (poursuite du calcul)', () => {
|
|
|
650
658
|
});
|
|
651
659
|
|
|
652
660
|
describe('calcul_3cl - surfaces immeuble / appartement et prorata', () => {
|
|
661
|
+
test('méthode application inconnue : surface habitable indéterminée transmise à la déperdition', () => {
|
|
662
|
+
enums.methode_application_dpe_log = { 1: 'méthode farfelue' };
|
|
663
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
664
|
+
const dpe = makeDpe();
|
|
665
|
+
|
|
666
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
667
|
+
|
|
668
|
+
// th null => aucune surface retenue (ni logement ni immeuble)
|
|
669
|
+
expect(calc_deperdition.mock.calls[0][2]).toBeNull();
|
|
670
|
+
expect(calc_deperdition.mock.calls[0][5]).toBeUndefined();
|
|
671
|
+
expect(errSpy).toHaveBeenCalled();
|
|
672
|
+
errSpy.mockRestore();
|
|
673
|
+
});
|
|
674
|
+
|
|
653
675
|
test('immeuble : Sh = surface immeuble ; ECS collective individualisée non divisée', () => {
|
|
654
676
|
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
655
677
|
const dpe = makeDpe();
|
|
@@ -1340,4 +1362,22 @@ describe('get_conso_coeff_1_7_2027', () => {
|
|
|
1340
1362
|
expect(res.ep_conso_5_usages_2027).toBeCloseTo(ep, 6);
|
|
1341
1363
|
expect(classe_bilan_dpe).toHaveBeenCalledWith(Math.floor(ep / 200), 'zc1', 'ca1', 200);
|
|
1342
1364
|
});
|
|
1365
|
+
|
|
1366
|
+
test('méthode application inconnue : surface indéterminée, aucune surface ne lui est substituée', () => {
|
|
1367
|
+
enums.methode_application_dpe_log = { 1: 'méthode farfelue' };
|
|
1368
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1369
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('G');
|
|
1370
|
+
const dpe = makeDpe();
|
|
1371
|
+
dpe.logement.sortie = {
|
|
1372
|
+
ep_conso: { ep_conso_5_usages: 10000 },
|
|
1373
|
+
ef_conso: { conso_5_usages: 6000 }
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
get_conso_coeff_1_7_2027(dpe);
|
|
1377
|
+
|
|
1378
|
+
// th null => ni surface logement ni surface immeuble
|
|
1379
|
+
expect(classe_bilan_dpe.mock.calls[0][3]).toBeUndefined();
|
|
1380
|
+
expect(errSpy).toHaveBeenCalled();
|
|
1381
|
+
errSpy.mockRestore();
|
|
1382
|
+
});
|
|
1343
1383
|
});
|
|
@@ -5,9 +5,11 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
5
5
|
// du comportement réel de `DpePreProcessor` (testé dans son propre spec).
|
|
6
6
|
const preprocessMock = vi.fn();
|
|
7
7
|
vi.mock('../../../preprocessor/index.js', () => ({
|
|
8
|
-
DpePreProcessor: vi.fn(
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
DpePreProcessor: vi.fn(function () {
|
|
9
|
+
return {
|
|
10
|
+
preprocess: preprocessMock
|
|
11
|
+
};
|
|
12
|
+
})
|
|
11
13
|
}));
|
|
12
14
|
|
|
13
15
|
const { DpeNormalizerService } = await import('./dpe-normalizer.service.js');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open3cl/engine",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "Open Source 3CL-DPE engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"qa:format": "prettier --plugin-search-dir . --write .",
|
|
27
27
|
"release": "semantic-release",
|
|
28
28
|
"release:dry": "semantic-release --dry-run",
|
|
29
|
-
"prepare": "husky
|
|
29
|
+
"prepare": "husky",
|
|
30
30
|
"assets:sync": "node scripts/synchronize_assets.js",
|
|
31
31
|
"version:update": "node scripts/update_version.js",
|
|
32
32
|
"reports:preview": "npm run http-server dist/reports/corpus -o",
|
|
@@ -45,43 +45,40 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"dioma": "^0.4.6",
|
|
48
|
-
"fast-xml-parser": "^5.11.
|
|
48
|
+
"fast-xml-parser": "^5.11.2",
|
|
49
49
|
"lodash-es": "^4.18.1",
|
|
50
50
|
"winston": "^3.19.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@aws-sdk/client-s3": "^3.
|
|
54
|
-
"@commitlint/cli": "^
|
|
55
|
-
"@commitlint/config-conventional": "^
|
|
56
|
-
"@commitlint/cz-commitlint": "^
|
|
57
|
-
"@duckdb/node-api": "^
|
|
58
|
-
"@
|
|
53
|
+
"@aws-sdk/client-s3": "^3.1160.0",
|
|
54
|
+
"@commitlint/cli": "^21.2.4",
|
|
55
|
+
"@commitlint/config-conventional": "^21.2.4",
|
|
56
|
+
"@commitlint/cz-commitlint": "^21.2.4",
|
|
57
|
+
"@duckdb/node-api": "^2.0.0-r.1",
|
|
58
|
+
"@eslint/js": "^10.0.1",
|
|
59
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
59
60
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
60
61
|
"@semantic-release/exec": "^7.1.0",
|
|
61
|
-
"@semantic-release/git": "^
|
|
62
|
-
"@semantic-release/github": "^12.0.
|
|
63
|
-
"@types/node": "^
|
|
64
|
-
"@vitest/coverage-v8": "^
|
|
62
|
+
"@semantic-release/git": "^11.0.1",
|
|
63
|
+
"@semantic-release/github": "^12.0.10",
|
|
64
|
+
"@types/node": "^24.19.2",
|
|
65
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
65
66
|
"ansi-colors": "^4.1.3",
|
|
66
67
|
"cli-progress": "^3.12.0",
|
|
67
68
|
"deep-object-diff": "^1.1.9",
|
|
68
|
-
"eslint": "^
|
|
69
|
-
"eslint-config-prettier": "^
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"eslint-plugin-import": "^2.29.1",
|
|
73
|
-
"eslint-plugin-n": "^16.6.2",
|
|
74
|
-
"eslint-plugin-promise": "^6.1.1",
|
|
75
|
-
"fast-csv": "^5.0.2",
|
|
69
|
+
"eslint": "^10.14.0",
|
|
70
|
+
"eslint-config-prettier": "^10.1.8",
|
|
71
|
+
"fast-csv": "^5.0.8",
|
|
72
|
+
"globals": "^17.13.0",
|
|
76
73
|
"http-server": "^14.1.1",
|
|
77
|
-
"husky": "^9.
|
|
78
|
-
"js-yaml": "^
|
|
79
|
-
"jscpd": "^
|
|
80
|
-
"piscina": "^5.
|
|
81
|
-
"prettier": "^3.
|
|
74
|
+
"husky": "^9.1.7",
|
|
75
|
+
"js-yaml": "^5.5.0",
|
|
76
|
+
"jscpd": "^5.4.2",
|
|
77
|
+
"piscina": "^5.3.2",
|
|
78
|
+
"prettier": "^3.9.10",
|
|
82
79
|
"pretty-quick": "^4.0.0",
|
|
83
|
-
"semantic-release": "^25.0.
|
|
84
|
-
"vitest": "
|
|
80
|
+
"semantic-release": "^25.0.9",
|
|
81
|
+
"vitest": "^5.0.5",
|
|
85
82
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
86
83
|
},
|
|
87
84
|
"author": "Open3Cl",
|
|
@@ -92,21 +89,5 @@
|
|
|
92
89
|
"bugs": {
|
|
93
90
|
"url": "https://github.com/Open3CL/engine/issues"
|
|
94
91
|
},
|
|
95
|
-
"homepage": "https://github.com/Open3CL/engine#readme"
|
|
96
|
-
"jscpd": {
|
|
97
|
-
"threshold": 0.1,
|
|
98
|
-
"reporters": [
|
|
99
|
-
"console",
|
|
100
|
-
"html"
|
|
101
|
-
],
|
|
102
|
-
"ignore": [
|
|
103
|
-
"**/*.spec.js"
|
|
104
|
-
],
|
|
105
|
-
"path": [
|
|
106
|
-
"src"
|
|
107
|
-
],
|
|
108
|
-
"output": "./duplicate",
|
|
109
|
-
"absolute": true,
|
|
110
|
-
"gitignore": true
|
|
111
|
-
}
|
|
92
|
+
"homepage": "https://github.com/Open3CL/engine#readme"
|
|
112
93
|
}
|
package/utils.js
CHANGED
|
@@ -459,12 +459,11 @@ export function tv(filePath, matcher) {
|
|
|
459
459
|
function tvLookup(filePath, matcher) {
|
|
460
460
|
const list = tvs[filePath];
|
|
461
461
|
const matcher_size = Object.keys(matcher).length;
|
|
462
|
-
let match_count = 0;
|
|
463
462
|
let max_match_count = 0;
|
|
464
463
|
let match = null;
|
|
465
464
|
|
|
466
465
|
for (const row of list) {
|
|
467
|
-
match_count = 0;
|
|
466
|
+
let match_count = 0;
|
|
468
467
|
for (const key in matcher) {
|
|
469
468
|
if (tvMatchImpl(row, key, matcher)) match_count += 1;
|
|
470
469
|
}
|
package/utils.spec.js
CHANGED
|
@@ -215,6 +215,14 @@ describe('Utils unit tests', () => {
|
|
|
215
215
|
expect(obj.other).toBe(3);
|
|
216
216
|
expect(obj.nested.enum_c).toBe('7');
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
test('ignore les propriétés héritées du prototype', () => {
|
|
220
|
+
const obj = Object.create({ enum_herite: 5 });
|
|
221
|
+
obj.enum_propre = 6;
|
|
222
|
+
useEnumAsString(obj);
|
|
223
|
+
expect(obj.enum_propre).toBe('6');
|
|
224
|
+
expect(obj.enum_herite).toBe(5);
|
|
225
|
+
});
|
|
218
226
|
});
|
|
219
227
|
|
|
220
228
|
// -------------------------------------------------------------------------------------------
|
|
@@ -589,6 +597,14 @@ describe('Utils unit tests', () => {
|
|
|
589
597
|
expect(obj.nested.remove_me).toBeUndefined();
|
|
590
598
|
expect(obj.nested.b).toBe(5);
|
|
591
599
|
});
|
|
600
|
+
|
|
601
|
+
test('ignore les propriétés héritées du prototype', () => {
|
|
602
|
+
const obj = Object.create({ remove_me: 1, herite: { remove_me: 2 } });
|
|
603
|
+
obj.propre = 1;
|
|
604
|
+
removeKeyFromJSON(obj, 'remove_me', []);
|
|
605
|
+
expect(obj.remove_me).toBe(1);
|
|
606
|
+
expect(obj.herite.remove_me).toBe(2);
|
|
607
|
+
});
|
|
592
608
|
});
|
|
593
609
|
|
|
594
610
|
// -------------------------------------------------------------------------------------------
|