@open3cl/engine 1.0.0

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.
Files changed (85) hide show
  1. package/10_besoin_fr.js +76 -0
  2. package/10_clim.js +45 -0
  3. package/11_besoin_ecs.js +25 -0
  4. package/11_ecs.js +95 -0
  5. package/11_nadeq.js +87 -0
  6. package/11_nadeq.spec.js +55 -0
  7. package/12.4_pac.js +54 -0
  8. package/13.2_generateur_combustion.js +295 -0
  9. package/13.2_generateur_combustion_bouilleur.js +173 -0
  10. package/13.2_generateur_combustion_ch.js +195 -0
  11. package/13.2_generateur_combustion_chaudiere.js +151 -0
  12. package/13.2_generateur_pac.js +36 -0
  13. package/13_rendement_distribution_ecs.js +12 -0
  14. package/14_generateur_ecs.js +388 -0
  15. package/15_conso_aux.js +257 -0
  16. package/16.2_production_enr.js +328 -0
  17. package/16.2_production_enr.spec.js +251 -0
  18. package/16_conso_eclairage.js +37 -0
  19. package/2021_04_13_confort_ete.js +61 -0
  20. package/2021_04_13_qualite_isolation.js +174 -0
  21. package/3.1_b.js +141 -0
  22. package/3.2.1_mur.js +331 -0
  23. package/3.2.1_mur.spec.js +46 -0
  24. package/3.2.2_plancher_bas.js +259 -0
  25. package/3.2.2_plancher_bas.spec.js +88 -0
  26. package/3.2.3_plancher_haut.js +158 -0
  27. package/3.3.1.4_porte.js +32 -0
  28. package/3.3_baie_vitree.js +308 -0
  29. package/3.3_baie_vitree.spec.js +333 -0
  30. package/3.4_pont_thermique.js +463 -0
  31. package/3_deperdition.js +258 -0
  32. package/4_ventilation.js +197 -0
  33. package/5_conso_ventilation.js +127 -0
  34. package/6.1_apport_gratuit.js +61 -0
  35. package/6.1_apport_gratuit.spec.js +181 -0
  36. package/6.2_surface_sud_equivalente.js +109 -0
  37. package/7_inertie.js +178 -0
  38. package/7_inertie.spec.js +263 -0
  39. package/8_intermittence.js +5 -0
  40. package/9_besoin_ch.js +198 -0
  41. package/9_chauffage.js +291 -0
  42. package/9_chauffage.spec.js +101 -0
  43. package/9_conso_ch.js +95 -0
  44. package/9_conso_ch.spec.js +255 -0
  45. package/9_emetteur_ch.js +122 -0
  46. package/9_generateur_ch.js +230 -0
  47. package/9_generateur_ch.spec.js +87 -0
  48. package/README.md +43 -0
  49. package/apport_et_besoin.js +55 -0
  50. package/conso.js +529 -0
  51. package/conso.spec.js +90 -0
  52. package/core/assets/domain/add-additionnal-ue-values-tables.js +57 -0
  53. package/core/assets/domain/synchronize-assets.js +29 -0
  54. package/core/assets/domain/synchronize-assets.spec.js +37 -0
  55. package/core/assets/domain/synchronize-c1-tables.js +61 -0
  56. package/core/assets/domain/synchronize-c1-tables.spec.js +35 -0
  57. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.js +73 -0
  58. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +72 -0
  59. package/core/assets/domain/synchronize-enum-tables.js +77 -0
  60. package/core/assets/domain/synchronize-enum-tables.spec.js +31 -0
  61. package/core/assets/domain/synchronize-solicitations-tables.js +72 -0
  62. package/core/assets/domain/synchronize-solicitations-tables.spec.js +47 -0
  63. package/core/assets/domain/synchronize-valeur-tables.js +146 -0
  64. package/core/assets/domain/synchronize-valeur-tables.spec.js +54 -0
  65. package/core/conf/infrastructure/application.config.js +33 -0
  66. package/core/file/infrastructure/adapter/file.store.js +75 -0
  67. package/core/file/infrastructure/adapter/file.store.spec.js +30 -0
  68. package/core/tv/infrastructure/assets/additional-ue-values.js +69 -0
  69. package/core/tv/infrastructure/tvs.store.js +40 -0
  70. package/core/tv/infrastructure/tvs.store.spec.js +34 -0
  71. package/core/util/infrastructure/object-util.js +23 -0
  72. package/core/util/infrastructure/object-util.spec.js +25 -0
  73. package/engine.js +503 -0
  74. package/enums.js +1155 -0
  75. package/ficheTechnique.js +86 -0
  76. package/ficheTechnique.spec.js +181 -0
  77. package/index.js +4 -0
  78. package/package.json +87 -0
  79. package/tv/18.2_sollicitations_ext.ods +0 -0
  80. package/tv/18.5_c1.ods +0 -0
  81. package/tv/dpe_ges_limit_values.ods +0 -0
  82. package/tv.js +80811 -0
  83. package/tvs.d.ts +7 -0
  84. package/utils.js +500 -0
  85. package/utils.spec.js +36 -0
package/tvs.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ interface RendementDistributionCh {
2
+ tv_rendement_distribution_ch_id: string;
3
+ enum_type_emission_distribution_id: string;
4
+ reseau_distribution: string;
5
+ reseau_distribution_isole: string;
6
+ rd: string;
7
+ }
package/utils.js ADDED
@@ -0,0 +1,500 @@
1
+ import enums from './enums.js';
2
+ import tvs from './tv.js';
3
+ import _ from 'lodash';
4
+
5
+ export let bug_for_bug_compat = false;
6
+ export function set_bug_for_bug_compat() {
7
+ bug_for_bug_compat = true;
8
+ }
9
+
10
+ export let tv_match_new_version = false;
11
+ export function set_tv_match_optimized_version() {
12
+ tv_match_new_version = true;
13
+ }
14
+
15
+ export function unset_tv_match_optimized_version() {
16
+ tv_match_new_version = false;
17
+ }
18
+
19
+ export const Tbase = {
20
+ 'inférieur à 400m': {
21
+ h1: -9.5,
22
+ h2: -6.5,
23
+ h3: -3.5
24
+ },
25
+ '400-800m': {
26
+ h1: -11.5,
27
+ h2: -8.5,
28
+ h3: -5.5
29
+ },
30
+ 'supérieur à 800m': {
31
+ h1: -13.5,
32
+ h2: -10.5,
33
+ h3: -7.5
34
+ }
35
+ };
36
+
37
+ export const Njj = {
38
+ Janvier: 31,
39
+ Février: 28,
40
+ Mars: 31,
41
+ Avril: 30,
42
+ Mai: 31,
43
+ Juin: 30,
44
+ Juillet: 31,
45
+ Aout: 31,
46
+ Septembre: 30,
47
+ Octobre: 31,
48
+ Novembre: 30,
49
+ Décembre: 24
50
+ };
51
+
52
+ // sum all Njj values
53
+ export const Njj_sum = Object.values(Njj).reduce((acc, val) => acc + val, 0);
54
+
55
+ export const mois_liste = [
56
+ 'Janvier',
57
+ 'Février',
58
+ 'Mars',
59
+ 'Avril',
60
+ 'Mai',
61
+ 'Juin',
62
+ 'Juillet',
63
+ 'Aout',
64
+ 'Septembre',
65
+ 'Octobre',
66
+ 'Novembre',
67
+ 'Décembre'
68
+ ];
69
+
70
+ export function add_references(enveloppe) {
71
+ let i = 0;
72
+ for (const mur of enveloppe.mur_collection.mur || []) {
73
+ if (!mur.donnee_entree.reference) {
74
+ mur.donnee_entree.reference = `mur_${i}`;
75
+ }
76
+ i += 1;
77
+ }
78
+ i = 0;
79
+ for (const ph of enveloppe.plancher_haut_collection.plancher_haut || []) {
80
+ if (!ph.donnee_entree.reference) {
81
+ ph.donnee_entree.reference = `plancher_haut_${i}`;
82
+ }
83
+ i += 1;
84
+ }
85
+ i = 0;
86
+ for (const pb of enveloppe.plancher_bas_collection.plancher_bas || []) {
87
+ if (!pb.donnee_entree.reference) {
88
+ pb.donnee_entree.reference = `plancher_bas_${i}`;
89
+ }
90
+ i += 1;
91
+ }
92
+ i = 0;
93
+ for (const bv of enveloppe.baie_vitree_collection.baie_vitree || []) {
94
+ if (!bv.donnee_entree.reference) {
95
+ bv.donnee_entree.reference = `baie_vitree_${i}`;
96
+ }
97
+ i += 1;
98
+ }
99
+ i = 0;
100
+ for (const porte of enveloppe.porte_collection.porte || []) {
101
+ if (!porte.donnee_entree.reference) {
102
+ porte.donnee_entree.reference = `porte_${i}`;
103
+ }
104
+ i += 1;
105
+ }
106
+ }
107
+
108
+ export function requestInputID(de, du, field, type) {
109
+ // enums
110
+ const enum_name = `enum_${field}_id`;
111
+ if (type) du[enum_name] = type;
112
+ else du[enum_name] = Object.keys(enums[field]);
113
+ return de[enum_name];
114
+ }
115
+
116
+ export function requestInput(de, du, field, type) {
117
+ if (enums.hasOwnProperty(field)) {
118
+ // enums
119
+ const enum_name = `enum_${field}_id`;
120
+ if (type) du[enum_name] = type;
121
+ else du[enum_name] = Object.keys(enums[field]);
122
+ return enums[field][de[enum_name]];
123
+ } else {
124
+ // not enums
125
+ if (!type) {
126
+ console.error(`requestInput: type is not defined for ${field}`);
127
+ return null;
128
+ }
129
+ du[field] = type;
130
+ return de[field];
131
+ }
132
+ }
133
+
134
+ export function getKeyByValue(object, value) {
135
+ return Object.keys(object).find((key) => object[key] === value);
136
+ }
137
+
138
+ export function tvColumnIDs(filePath, field) {
139
+ const list = tvs[filePath];
140
+ const enum_name = `enum_${field}_id`;
141
+ let ids = list.map((row) => row[enum_name]);
142
+ // remove undefineds
143
+ ids = ids.filter((id) => id);
144
+ // split each by | and get uniques
145
+ const unique_ids = [];
146
+ for (const id of ids) {
147
+ const values = id.split('|');
148
+ for (const value of values) {
149
+ if (!unique_ids.includes(value)) unique_ids.push(value);
150
+ }
151
+ }
152
+ // sort like numbers
153
+ return unique_ids;
154
+ }
155
+
156
+ export function tvColumnLines(filePath, column, matcher) {
157
+ const list = tvs[filePath];
158
+ // find lines in list that match matcher
159
+ const lines = [];
160
+ for (const row of list) {
161
+ let match = true;
162
+ for (const key in matcher) {
163
+ if (tvMatch(row, key, matcher) === false) {
164
+ match = false;
165
+ break;
166
+ }
167
+ }
168
+ if (match) lines.push(row[column]);
169
+ }
170
+ return lines;
171
+ }
172
+
173
+ function tvMatch(row, key, matcher) {
174
+ if (tv_match_new_version) {
175
+ return tvMatchOptimized(row, key, matcher);
176
+ }
177
+ if (!row.hasOwnProperty(key)) {
178
+ // for empty csv columns
179
+ // for q4pa_conv
180
+ return false;
181
+ }
182
+
183
+ let match_value = String(matcher[key]).toLowerCase();
184
+
185
+ // If the match value starts with ^, ends with $ and contains + then we escape the +
186
+ // Ex: ^iti+ite$ becomes ^iti\\+ite$
187
+ if (/^\^(.*)\+(.*)\$$/g.test(match_value)) {
188
+ match_value = match_value.replace('+', '\\+');
189
+ }
190
+
191
+ if (key.startsWith('enum_')) match_value = `^${String(matcher[key]).toLowerCase()}$`;
192
+
193
+ if (row[key].includes('|')) {
194
+ const values = row[key].split('|');
195
+ if (!values.some((v) => v.toLowerCase().match(String(match_value)))) {
196
+ return false;
197
+ }
198
+ } else if (Number.isInteger(matcher[key]) && ['≥', '≤'].some((char) => row[key].includes(char))) {
199
+ return eval(match_value + row[key].replace('≥', '>=').replace('≤', '<='));
200
+ } else if (!row[key].toLowerCase().match(String(match_value))) {
201
+ return false;
202
+ }
203
+ return true;
204
+ }
205
+
206
+ function tvMatchOptimized(row, key, matcher) {
207
+ if (!row[key]) {
208
+ // for empty csv columns
209
+ // for q4pa_conv
210
+ return false;
211
+ }
212
+
213
+ let row_value = row[key].toLowerCase();
214
+ let match_value = String(matcher[key]).toLowerCase();
215
+
216
+ if (row_value === match_value) {
217
+ return true;
218
+ }
219
+
220
+ if (match_value.startsWith('^')) {
221
+ const match_value_no_regex = match_value.replace('^', '').replace('$', '');
222
+ if (row_value === match_value_no_regex) {
223
+ return true;
224
+ }
225
+ }
226
+
227
+ if (isNaN(matcher[key]) && row_value.includes(match_value)) {
228
+ return true;
229
+ }
230
+
231
+ if (row_value.includes('|')) {
232
+ return row_value.split('|').includes(match_value);
233
+ }
234
+
235
+ if (Number.isInteger(matcher[key]) && ['≥', '≤'].some((char) => row[key].includes(char))) {
236
+ return eval(match_value + row[key].replace('≥', '>=').replace('≤', '<='));
237
+ }
238
+
239
+ return row_value.includes(match_value);
240
+ }
241
+
242
+ export function tv(filePath, matcher) {
243
+ const list = tvs[filePath];
244
+ let match_count = 0;
245
+ let max_match_count = 0;
246
+ let match = null;
247
+
248
+ for (const row of list) {
249
+ match_count = 0;
250
+ for (const key in matcher) {
251
+ if (tvMatch(row, key, matcher)) match_count += 1;
252
+ }
253
+ // if match_count is same as matcher, we are done
254
+ if (match_count === Object.keys(matcher).length) return row;
255
+
256
+ /* if (filePath === 'q4pa_conv') console.warn(match_count) */
257
+ if (match_count > max_match_count) {
258
+ max_match_count = match_count;
259
+ match = row;
260
+ }
261
+ }
262
+ /* if (filePath === 'pont_thermique') { */
263
+ /* console.warn(matcher) */
264
+ /* console.warn(match) */
265
+ /* } */
266
+ return match;
267
+ }
268
+
269
+ export function removeKeyFromJSON(jsonObj, keyToRemove, skipKeys) {
270
+ for (const key in jsonObj) {
271
+ if (skipKeys.includes(key)) continue;
272
+ if (jsonObj.hasOwnProperty(key)) {
273
+ if (key === keyToRemove) {
274
+ delete jsonObj[key];
275
+ } else if (typeof jsonObj[key] === 'object') {
276
+ removeKeyFromJSON(jsonObj[key], keyToRemove, skipKeys);
277
+ }
278
+ }
279
+ }
280
+ }
281
+
282
+ export function clean_dpe(dpe_in) {
283
+ // skip generateur_[ecs|chauffage] because some input data is contained in donnee_intermediaire (e.g. pn, qp0, ...)
284
+ removeKeyFromJSON(dpe_in, 'donnee_intermediaire', ['generateur_ecs', 'generateur_chauffage']);
285
+ _.set(dpe_in, 'logement.sortie', null);
286
+ }
287
+
288
+ export function sanitize_dpe(dpe_in) {
289
+ const collection_paths = [
290
+ 'logement.enveloppe.plancher_bas_collection.plancher_bas',
291
+ 'logement.enveloppe.plancher_haut_collection.plancher_haut',
292
+ 'logement.ventilation_collection.ventilation',
293
+ 'logement.climatisation_collection.climatisation',
294
+ 'logement.enveloppe.baie_vitree_collection.baie_vitree',
295
+ 'logement.enveloppe.porte_collection.porte',
296
+ 'logement.enveloppe.pont_thermique_collection.pont_thermique'
297
+ ];
298
+ for (const path of collection_paths) {
299
+ if (!_.has(dpe_in, path)) {
300
+ _.set(dpe_in, path, []);
301
+ }
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Retrieve a number describing a thickness from the description
307
+ * @param description string in which to get the number
308
+ * @returns {number} if found, 0 otherwise
309
+ */
310
+ export function getThicknessFromDescription(description) {
311
+ if (!description) {
312
+ return 0;
313
+ }
314
+
315
+ const matching = description.match(/(\d+) cm/);
316
+ return matching && matching.length > 1 ? Number.parseFloat(matching[1]) : 0;
317
+ }
318
+
319
+ /**
320
+ * Return true si la collection $type peut être vide
321
+ * - Pas de pont thermique ou pas de pont thermique de type enum_type_liaison_id
322
+ * - Déperdition pour $type === 0 (donne probablement sur un autre local chauffé)
323
+ * @param logement {Logement}
324
+ * @param type {string}
325
+ * @param enum_type_liaison_id {number}
326
+ * @returns {boolean}
327
+ */
328
+ export function collectionCanBeEmpty(logement, type, enum_type_liaison_id) {
329
+ const pontsThermiques = logement.enveloppe.pont_thermique_collection.pont_thermique || [];
330
+
331
+ const pontsThermiquesWithLiaison = pontsThermiques.filter(
332
+ (pontThermique) => pontThermique.donnee_entree.enum_type_liaison_id === enum_type_liaison_id
333
+ );
334
+
335
+ const deperdition = logement.sortie.deperdition[`deperdition_${type}`];
336
+
337
+ return pontsThermiquesWithLiaison.length === 0 && deperdition === 0;
338
+ }
339
+
340
+ /**
341
+ * Retrieve a number describing a volume from the description
342
+ * @param description string in which to get the number
343
+ * @returns {number} if found, 0 otherwise
344
+ */
345
+ export function getVolumeStockageFromDescription(description) {
346
+ if (!description) {
347
+ return 0;
348
+ }
349
+
350
+ const matching = description.split('contenance ballon ');
351
+ return matching && matching.length > 1 ? parseInt(matching[1], 10) : 0;
352
+ }
353
+
354
+ /**
355
+ * Remove space and accented characters
356
+ * @param reference {string}
357
+ * return {string}
358
+ */
359
+ export function cleanReference(reference) {
360
+ if (reference) {
361
+ return reference
362
+ .toString()
363
+ .toLowerCase()
364
+ .normalize('NFD')
365
+ .replace(/[\u0300-\u036f]/g, '')
366
+ .replace(/\s+/g, '');
367
+ }
368
+
369
+ return reference;
370
+ }
371
+
372
+ /**
373
+ * Return true if references are the same (without spaces and accented characters)
374
+ * @param reference1 {string}
375
+ * @param reference2 {string}
376
+ * return {boolean}
377
+ */
378
+ export function compareReferences(reference1, reference2) {
379
+ return cleanReference(reference1) === cleanReference(reference2);
380
+ }
381
+
382
+ /**
383
+ * Vérification si le chauffage est par effet joule
384
+ * 3.2 Calcul des U des parois opaques
385
+ * On considère qu’un logement est chauffé par effet joule lorsque la chaleur est fournie par une résistance électrique.
386
+ * @param instal_ch {InstallationChauffageItem[]}
387
+ * @returns {string} 1 if effet joule, 0 otherwise
388
+ */
389
+ export function isEffetJoule(instal_ch) {
390
+ const { surfaceEffetJoule, surfaceTotale } = instal_ch.reduce(
391
+ (acc, item) => {
392
+ const generatorIds = item.generateur_chauffage_collection.generateur_chauffage.reduce(
393
+ (acc, generateur) => {
394
+ return [...acc, generateur.donnee_entree.enum_type_generateur_ch_id];
395
+ },
396
+ []
397
+ );
398
+
399
+ /**
400
+ * enum_type_generateur_ch_id
401
+ * 98 - convecteur électrique nfc, nf** et nf***
402
+ * 99 - panneau rayonnant électrique nfc, nf** et nf***
403
+ * 100 - radiateur électrique nfc, nf** et nf***
404
+ * 101 - autres émetteurs à effet joule
405
+ * 102 - plancher ou plafond rayonnant électrique avec régulation terminale
406
+ * 103 - plancher ou plafond rayonnant électrique sans régulation terminale
407
+ * 104 - radiateur électrique à accumulation
408
+ * 105 - convecteur bi-jonction
409
+ * 106 - chaudière électrique
410
+ * @type {boolean}
411
+ */
412
+ const isEffetJoule =
413
+ generatorIds.filter((value) =>
414
+ ['98', '99', '100', '101', '102', '103', '104', '105', '106'].includes(value)
415
+ ).length > 0;
416
+
417
+ if (isEffetJoule) {
418
+ acc.surfaceEffetJoule += item.donnee_entree.surface_chauffee;
419
+ }
420
+
421
+ acc.surfaceTotale += item.donnee_entree.surface_chauffee;
422
+ return acc;
423
+ },
424
+ { surfaceEffetJoule: 0, surfaceTotale: 0 }
425
+ );
426
+
427
+ // Si la surface chauffée par une résistance électrique est majoritaire => effet_joule = 1
428
+ return surfaceEffetJoule / surfaceTotale >= 0.5 ? '1' : '0';
429
+ }
430
+
431
+ /**
432
+ * Vérification si on trouve au moins une des chaînes de caractères substrings dans mainString
433
+ * @param mainString chaîne dans laquelle vérifier l'existence des substrings
434
+ * @param substrings chaîne à chercher dans la chaine principale
435
+ * @returns {boolean}
436
+ */
437
+ export function containsAnySubstring(mainString, substrings) {
438
+ return substrings.some((substring) =>
439
+ mainString.toString().toLowerCase().includes(substring.toLowerCase())
440
+ );
441
+ }
442
+
443
+ /**
444
+ * Conversion des expressions du type `70 < Pn <= 400` en (70 < Pn) && (Pn <= 400)
445
+ * @param expression {string}
446
+ * @returns {string}
447
+ */
448
+ export function convertExpression(expression) {
449
+ if (!expression) {
450
+ return expression;
451
+ }
452
+
453
+ // Gère les expressions combinées comme `70 < Pn <= 400`
454
+ expression = expression.replace(
455
+ /(\d+)\s*<\s*([a-zA-Z_$][\w]*)\s*<=\s*(\d+)/g,
456
+ '($1 < $2) && ($2 <= $3)'
457
+ );
458
+ return expression;
459
+ }
460
+
461
+ /**
462
+ * Retourne les valeurs qui encadrent inputNumber dans ranges
463
+ * Si inputNumber est présent dans ranges, inputNumber est retourné comme borne 1 et borne 2
464
+ *
465
+ * @param inputNumber {float}
466
+ * @param ranges {number[]}
467
+ * @returns {*[]}
468
+ */
469
+ export function getRange(inputNumber, ranges) {
470
+ const result = [];
471
+
472
+ ranges.sort();
473
+
474
+ if (inputNumber < ranges[0]) {
475
+ result.push(ranges[0]);
476
+ result.push(ranges[1]);
477
+ return result;
478
+ }
479
+ if (inputNumber > ranges[ranges.length - 1]) {
480
+ result.push(ranges[ranges.length - 2]);
481
+ result.push(ranges[ranges.length - 1]);
482
+ }
483
+ if (ranges.includes(inputNumber)) {
484
+ result.push(inputNumber);
485
+ result.push(inputNumber);
486
+ } else {
487
+ ranges.find((range, index) => {
488
+ if (inputNumber < range) {
489
+ if (index > 0) {
490
+ result.push(ranges[index - 1]);
491
+ } else {
492
+ result.push(ranges[index]);
493
+ }
494
+ result.push(ranges[index]);
495
+ return true;
496
+ }
497
+ });
498
+ }
499
+ return result;
500
+ }
package/utils.spec.js ADDED
@@ -0,0 +1,36 @@
1
+ import { convertExpression, getRange, getThicknessFromDescription } from './utils.js';
2
+
3
+ describe('Utils unit tests', () => {
4
+ it.each([
5
+ [0, null],
6
+ [0, undefined],
7
+ [0, ''],
8
+ [0, 'Mur en blocs de béton creux'],
9
+ [0, "Mur en blocs de béton creux d'épaisseur xxx cm non isolé"],
10
+ [4, "Mur en blocs de béton creux d'épaisseur 4 cm non isolé"],
11
+ [25, "Mur en blocs de béton creux d'&apos;'épaisseur ≥ 25 cm non isolé"]
12
+ ])('should get thickness %s from description %s', (thickness, description) => {
13
+ expect(getThicknessFromDescription(description)).toBe(thickness);
14
+ });
15
+
16
+ it.each([
17
+ ['70 < Pn <= 400', '(70 < Pn) && (Pn <= 400)'],
18
+ ['70 < Pn', '70 < Pn'],
19
+ ['Pn <= 400', 'Pn <= 400'],
20
+ ['Pn == 400', 'Pn == 400'],
21
+ ['Pn', 'Pn'],
22
+ [null, null],
23
+ [undefined, undefined]
24
+ ])('should transform expression %s to %s', (expression, expected) => {
25
+ expect(convertExpression(expression)).toBe(expected);
26
+ });
27
+
28
+ it.each([
29
+ [[1, 1.2, 3.4, 5.6], 0.5, [1, 1.2]],
30
+ [[1, 1.2, 3.4, 5.6], 1, [1, 1]],
31
+ [[1, 1.2, 3.4, 5.6], 1.3, [1.2, 3.4]],
32
+ [[1, 1.2, 3.4, 5.6], 6.5, [3.4, 5.6]]
33
+ ])('should for values %s and inputNumber %s return range %s', (ranges, inputNumber, expected) => {
34
+ expect(getRange(inputNumber, ranges)).toStrictEqual(expected);
35
+ });
36
+ });