From 0c3ad2e6f44115978b2ac2d70f2d20a2d3301c17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ma=C3=AFeul=20Rouquette?= <maieul@maieul.net>
Date: Wed, 18 Oct 2023 18:27:34 +0200
Subject: [PATCH] =?UTF-8?q?fix(#331):=20ajuster=20le=20libell=C3=A9=20de?=
 =?UTF-8?q?=20l'=C3=A9tape=20suivante=20en=20fonction=20des=20`afficher=5F?=
 =?UTF-8?q?si`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Pour cela on met un attribut html `data-resume_etapes_futures` sur le `<form>`.

Ce résumé est envoyé au squelette sous forme de tableau PHP, puis converti en JSON.

Il est calculé dynamiquement en fonction des options globales du
squelette.

Cet attribut sert également pour le calcul des `afficher_si` des étapes
suivantes dans le fil d'ariane des étapes, ainsi que pour le calcul du
nombre total d'étape. On supprime par conséquent les attributs
`data-afficher_si` sur les `<li>` d'étape et
`data-afficher_si-etapes-futures` sur le `div.formulaire_spip__etapes--courante`.

On simplifie (et debug) le code squelette de masquage des étapes passées
qui ne répondaient pas aux `afficher_si`.

On ajuste le JS en conséquent.

fix #331
---
 formulaires/inc-saisies-cvt-boutons.html      |   8 +-
 .../inc-saisies-cvt-etapes-courante.html      |   6 +-
 .../inc-saisies-cvt-etapes-defaut.html        |  11 +-
 formulaires/inc-saisies-cvt.html              |   2 +-
 inc/saisies_afficher_si_js.php                |   1 +
 inc/saisies_formulaire.php                    |  44 ++++
 javascript/saisies_afficher_si.js             |  76 ++++++-
 saisies_pipelines.php                         |  15 +-
 tests/FormulaireTest.php                      | 206 ++++++++++++++++++
 tests/bootstrap.php                           |   8 +
 10 files changed, 353 insertions(+), 24 deletions(-)

diff --git a/formulaires/inc-saisies-cvt-boutons.html b/formulaires/inc-saisies-cvt-boutons.html
index b7528f52..3e747d04 100644
--- a/formulaires/inc-saisies-cvt-boutons.html
+++ b/formulaires/inc-saisies-cvt-boutons.html
@@ -1,11 +1,10 @@
 <p class="boutons" [ data-afficher_si="(#ENV*{_saisies/options/afficher_si_submit}|saisies_afficher_si_js{#ENV{_saisies}})"]>
 	<span class="image_loading"></span>
 	[(#SET{label_valider, #ENV*{_saisies/options/texte_submit, #ENV*{saisies_texte_submit, <:bouton_enregistrer:>}}})]
-
+	#SET{label_enregistrer,#GET{label_valider}}
 
 	[(#ENV{_etape}|et{#ENV{_etape}|!={#ENV{_etapes}}}|oui)
 		[(#SET{label_valider, #ENV*{_saisies/options/etapes_suivant, <:bouton_suivant:>}})]
-
 		[(#ENV*{_saisies/options/etapes_precedent_suivant_titrer}|oui)
 			#SET{etape,#ENV*{_saisies_par_etapes/etape_#VAL{#ENV{_etape}|plus{1}}}
 			[(#SET{etape_suivante_titre,#INCLURE{
@@ -13,12 +12,11 @@
 				label=#GET{etape/options/label},
 				icone=#GET{etape/options/icone},
 				taille_icone=#GET{etape/options/taille_icone}
-		}|sinon{<:saisies:etapes_recapitulatif_label:>}})]
+				}|sinon{<:saisies:etapes_recapitulatif_label:>}})]
 			#SET{label_valider,#GET{label_valider}|trim|label_ponctuer|concat{' ',#GET{etape_suivante_titre}|wrap{'<span class="titre-etape">'}}}
 		]
 	]
 
-
 	[(#ENV{_etape}|>{1}|oui)
 		[(#SET{label_precedent,#ENV*{_saisies/options/etapes_precedent, <:precedent|ucfirst:>}})]
 		[(#ENV*{_saisies/options/etapes_precedent_suivant_titrer}|oui)
@@ -33,5 +31,5 @@
 		]
 		<button type="submit" class="submit submit_precedent" name="aller_a_etape" value="[(#ENV{_saisies}|saisies_determiner_recul_rapide{#ENV{_etape}})]"><span class="btn__label">[(#GET{label_precedent}|_T_ou_typo)]</span></button>
 	]
-	<button type="submit" class="submit[ (#ENV{_etapes}|?{submit_suivant,submit_main})]" value="1"><span class="btn__label">[(#GET{label_valider}|_T_ou_typo)]</span></button>
+	<button type="submit" class="submit[ (#ENV{_etapes}|?{submit_suivant,submit_main})]" value="1" data-label_enregistrer="#GET{label_enregistrer}"><span class="btn__label">[(#GET{label_valider}|_T_ou_typo)]</span></button>
 </p>
diff --git a/formulaires/inc-saisies-cvt-etapes-courante.html b/formulaires/inc-saisies-cvt-etapes-courante.html
index 7875d73f..b0389d2c 100644
--- a/formulaires/inc-saisies-cvt-etapes-courante.html
+++ b/formulaires/inc-saisies-cvt-etapes-courante.html
@@ -25,19 +25,17 @@
 ]
 [(#REM) Mettre toutes les étapes futures]
 [(#GET{etape_courante}|>{#ENV{_etape}}|oui)
-	[(#VALEUR{options/afficher_si}|oui)
-		#SET{afficher_si,#GET{afficher_si}|array_merge{#ARRAY{#CLE,#VAL{&quot;}|str_replace{'"',#VALEUR{options/afficher_si}|saisies_afficher_si_js{#ENV{etapes}}}}}}
-	]
 	#SET{etapes_max,#GET{etapes_max}|plus{1}}
 ]
 </BOUCLE_etapes>
+
 	#SET{params, #ARRAY{
 			'etape',<span class="formulaire_spip_etape__courante">#GET{etape_a_afficher}</span>,
 			'etapes', <span class="formulaire_spip_etape__total">#GET{etapes_max}</span>,
 			'label_etape',#GET{etape_a_afficher_label}
 		}
 	}
-<div class="formulaire_spip__etapes etapes formulaire_spip__etapes--courante" [data-afficher_si-etapes-futures="(#VAL{'"'}|str_replace{'&quot;', #GET{afficher_si}|json_encode})"] data-etapes_max="#GET{etapes_max}">
+<div class="formulaire_spip__etapes etapes formulaire_spip__etapes--courante" data-etapes_max="#GET{etapes_max}">
 	[(#VAL{saisies:cvt_etapes_courante}|_T{#GET{params}})]
 </div>
 </B_etapes>
diff --git a/formulaires/inc-saisies-cvt-etapes-defaut.html b/formulaires/inc-saisies-cvt-etapes-defaut.html
index c784fe3d..4d10af7d 100644
--- a/formulaires/inc-saisies-cvt-etapes-defaut.html
+++ b/formulaires/inc-saisies-cvt-etapes-defaut.html
@@ -5,15 +5,12 @@
 		<BOUCLE_etapes(DATA){source table, #ENV{etapes}}>
 		#SET{etape,#CLE|replace{etape_,''}}
 		#SET{afficher_si,#VALEUR{options/afficher_si}|saisies_afficher_si_js{#ENV{etapes}}}
-		#SET{afficher,non}
-		[(#VALEUR|in_any{#GET{afficher_si_masquees}}|non|ou{#ENV{_etape}|<{#GET{etape}}})
-			#SET{afficher,oui}
-			[(#GET{afficher_si}|saisies_afficher_si_js_est_statique|oui)
-				#SET{afficher,#GET{afficher_si}|saisies_evaluer_afficher_si}
-			]
+		#SET{afficher,oui}
+		[(#VALEUR|in_any{#GET{afficher_si_masquees}}|oui)
+			#SET{afficher,''}
 		]
 		[(#GET{afficher}|oui)
-			<li [data-afficher_si="(#GET{afficher_si})"] class="etapes__item"[(#ENV{_etape}|=={#GET{etape}}|oui) aria-current="step"]>
+			<li class="etapes__item #CLE" [(#ENV{_etape}|=={#GET{etape}}|oui) aria-current="step"]>
 				#SET{label_etape,#INCLURE{
 					fond=inclure/fieldset_legend,
 					label=#VALEUR{options/label},
diff --git a/formulaires/inc-saisies-cvt.html b/formulaires/inc-saisies-cvt.html
index 8f16e9d1..39faf057 100644
--- a/formulaires/inc-saisies-cvt.html
+++ b/formulaires/inc-saisies-cvt.html
@@ -3,7 +3,7 @@
 	[<p class="reponse_formulaire reponse_formulaire_ok"[(#HTML5|oui) role="status"]>(#ENV**{message_ok})</p>]
 	[<p class="reponse_formulaire reponse_formulaire_erreur"[(#HTML5|oui) role="alert"]>(#ENV**{message_erreur})</p>]
 	[(#ENV{editable}|oui)
-	<form method="post" action="#ENV{action}" enctype="multipart/form-data"><div>
+	<form method="post" action="#ENV{action}" enctype="multipart/form-data"[ data-resume_etapes_futures="(#ENV{_resume_etapes_futures}|json_encode|attribut_html)"]><div>
 		[(#REM) declarer les hidden qui declencheront le service du formulaire
 		parametre : url d'action ]
 		#ACTION_FORMULAIRE{#ENV{action}}
diff --git a/inc/saisies_afficher_si_js.php b/inc/saisies_afficher_si_js.php
index 9c6f5c00..522de1d0 100644
--- a/inc/saisies_afficher_si_js.php
+++ b/inc/saisies_afficher_si_js.php
@@ -114,3 +114,4 @@ function saisies_afficher_si_js_est_statique($test) {
 		return true;
 	}
 }
+
diff --git a/inc/saisies_formulaire.php b/inc/saisies_formulaire.php
index 8c2ee270..e1b1eed3 100644
--- a/inc/saisies_formulaire.php
+++ b/inc/saisies_formulaire.php
@@ -124,3 +124,47 @@ function saisies_determiner_deplacement_rapide(array $saisies, int $etape, int $
 	}
 	return $i;
 }
+
+/**
+ * Retourne un résumé des étapes futures (qu'elles s'afficheront finalement ou pas).
+ * @param array $etapes liste des étapes (sous forme de tableau de saisies)
+ * @param int $etape etape courante
+ * @param array $options_demandees tableau décrivant les options qu'on demande
+ * @return array [
+ *	'etape_n' => ['info_x' => 'valeur_x', 'info_y' => 'valeur_y' …]
+ *	…
+ * ]
+**/
+function saisies_resumer_etapes_futures(array $etapes, int $etape, array $options_demandees): array {
+	$return = [];
+	foreach ($etapes as $e => $description) {
+		$numero_etape = intval(str_replace('etape_', '', $e));
+		if ($numero_etape > $etape) {
+			$output_etape = [];
+
+			foreach ($options_demandees as $option) {
+				if (isset($description['options'][$option])) {
+					$output_etape[$option] = $description['options'][$option];
+				}
+			}
+
+			$return[$e] = $output_etape;
+		}
+	}
+	return $return;
+}
+
+/**
+ * Détermine quels options d'étapes sont utiles au résumé des étapes futures
+ * En fonction des options globales du formulaire
+ * @param array $options_globales optiosn globales du formulaire
+ * @return array liste des options à garder
+**/
+function saisies_determiner_options_demandees_resumer_etapes_futures(array $options_globales): array {
+	$options = ['afficher_si'];// on veut dans tous les cas les afficher_si
+
+	if (in_array($options_globales['etapes_precedent_suivant_titrer'] ?? '', ['on', true], true)) {
+		$options[] = 'label';
+	}
+	return $options;
+}
diff --git a/javascript/saisies_afficher_si.js b/javascript/saisies_afficher_si.js
index a7923834..7b977c99 100644
--- a/javascript/saisies_afficher_si.js
+++ b/javascript/saisies_afficher_si.js
@@ -6,11 +6,13 @@ afficher_si_current_data = '';
 function afficher_si_init() {
 	$('form:not([data-afficher_si-init])').each(function(){
 		// Seulement si au moins un afficher_si dedans !
-		if ($(this).find('[data-afficher_si]').length !== 0 || $(this).find('[data-afficher_si-etapes-futures]') !== 0) {
+		if ($(this).find('[data-afficher_si]').length !== 0 || $(this).find('[data-resume_etapes_futures]') !== 0) {
 			form = $(this);
 			form.find('.formulaire_spip__etapes').each(function() {
 				$(this).css('min-height', $(this).height());
 			});
+			afficher_si_init_chemin_etapes(form);
+
 			afficher_si_set_current_data(form);
 			form.find('[data-afficher_si]').each(function(){
 				condition = verifier_afficher_si($(this), true);
@@ -18,6 +20,7 @@ function afficher_si_init() {
 			}
 			);
 			afficher_si_set_etapes_presentation_courante(form);
+			afficher_si_set_etape_suivante(form);
 
 			// Un écouteur sur la modif de tous les champs de ce form
 			$(this).find('textarea, input, select').change(function(){
@@ -34,6 +37,7 @@ function afficher_si_init() {
 						animer_afficher_si($(this), condition);
 					})
 					afficher_si_set_etapes_presentation_courante(form, name);
+					afficher_si_set_etape_suivante(form, name);
 				}
 			});
 			$(this).attr('data-afficher_si-init', true);
@@ -167,11 +171,14 @@ function afficher_si(args) {
 //@param form, le formulaire
 //@param name le nom de la saisie dont la valeur vient juste de changer
 function afficher_si_set_etapes_presentation_courante(form, name='') {
-	form.find('.formulaire_spip__etapes--courante[data-afficher_si-etapes-futures]').each(function() {
-		afficher_si_etapes = JSON.parse($(this).attr('data-afficher_si-etapes-futures'));
-		etape_total = $(this).attr('data-etapes_max');
-		for (etape in afficher_si_etapes) {
-			condition = afficher_si_etapes[etape];
+	var etapes = afficher_si_parse_data_etapes_futures(form);
+	if (!etapes) {
+		return;
+	}
+	form.find('[data-etapes_max]').each(function() {
+		var etape_total = $(this).attr('data-etapes_max');
+		for (etape in etapes) {
+			var condition = etapes[etape]['afficher_si'] ?? 'true';
 			if (!name || condition.includes(name)) {
 				$(this).attr('data-' + etape, eval(condition));
 			}
@@ -182,3 +189,60 @@ function afficher_si_set_etapes_presentation_courante(form, name='') {
 		$(this).find('.formulaire_spip_etape__total').text(etape_total);
 	});
 }
+
+// Pour le libellé de l'étape suivante
+//@param form, le formulaire
+//@param name le nom de la saisie dont la valeur vient juste de changer
+function afficher_si_set_etape_suivante(form, name) {
+	var etapes = afficher_si_parse_data_etapes_futures(form);
+	if (!etapes) {
+		return;
+	}
+	var label_enregistrer = form.find('button.submit_suivant').attr('data-label_enregistrer');
+	var titre_retenu = label_enregistrer;
+	// Chercher la première future étape
+	for (etape in etapes) {
+		var afficher_si_etape = etapes[etape]['afficher_si'] ?? 'true';
+		if (eval(afficher_si_etape)) {
+			titre_retenu = etapes[etape]['label'];
+			break;
+		}
+	}
+	form.find('button.submit_suivant').each(function() {
+		var $span = $(this).find('.btn__label');
+		// Stocker le modèle pour suivant, si pas deja fait
+		if (!$(this).attr('data-modele')) {
+			$(this).attr('data-modele', $span.html());
+		}
+		// Puis ajuster le titre, le modèle variant selon que nous passons directement à la validation ou pas
+		if (titre_retenu == label_enregistrer) {
+			$span.html(titre_retenu);
+		} else {
+			$span.html($(this).attr('data-modele'));
+			$span.find('.titre-etape').html(titre_retenu);
+		}
+	});
+}
+// Recopier les info d'afficher_si présente dans [data-resume_etapes_futures] au sein de chaque etapes futures
+// Le but est de simplifier ainsi le code principal, en se contentant du code de animer_afficher_si()
+// @param jquery obcet form
+function afficher_si_init_chemin_etapes(form) {
+	var etapes = afficher_si_parse_data_etapes_futures(form);
+	if (!etapes) {
+		return;
+	}
+	for (etape in etapes) {
+		var afficher_si_etape = etapes[etape]['afficher_si'] ?? '';
+		if (afficher_si_etape) {
+			form.find('.etapes__item.' + etape).attr('data-afficher_si', afficher_si_etape);
+		}
+	}
+}
+function afficher_si_parse_data_etapes_futures(form) {
+	var data = form.attr('data-resume_etapes_futures');
+	if (!data) {
+		return;
+	}
+	var etapes = JSON.parse(data);
+	return etapes;
+}
diff --git a/saisies_pipelines.php b/saisies_pipelines.php
index 0661c7e2..59b2d8fa 100644
--- a/saisies_pipelines.php
+++ b/saisies_pipelines.php
@@ -169,8 +169,21 @@ function saisies_formulaire_charger($flux) {
 		if ($etapes = saisies_lister_par_etapes($saisies)) {
 			$flux['data']['_etapes'] = count($etapes);
 			$flux['data']['_saisies_par_etapes'] = $etapes;
-		}
 
+			// Construction du tableau resumé des étapes futures
+			$options_resume = saisies_determiner_options_demandees_resumer_etapes_futures($saisies['options']);
+			$resume_etapes_futures = saisies_resumer_etapes_futures($etapes, _request('_etape') ?? 1, $options_resume);
+			// Convertir les afficher_si en code JS
+			$resume_etapes_futures = array_map(function ($i) use ($etapes) {
+				if (!isset($i['afficher_si'])) {
+					return $i;
+				}
+				$i['afficher_si'] = saisies_afficher_si_js($i['afficher_si'], $etapes);
+				$i['afficher_si'] = str_replace('&quot;', '"', $i['afficher_si']);// Pour éviter que les &quot; soit à nouveau encodé par json_encode, ce qui fout la merde au decodage en JS
+			  return $i;
+			}, $resume_etapes_futures);
+			$flux['data']['_resume_etapes_futures'] = $resume_etapes_futures;
+		}
 		// On ajoute le tableau complet des saisies
 		$flux['data']['_saisies'] = $saisies;
 
diff --git a/tests/FormulaireTest.php b/tests/FormulaireTest.php
index cc155a87..4c492fd1 100644
--- a/tests/FormulaireTest.php
+++ b/tests/FormulaireTest.php
@@ -11,6 +11,8 @@ use PHPUnit\Framework\Attributes\RunInSeparateProcess;
  * @covers saisies_determiner_deplacement_rapide()
  * @covers saisies_determiner_avance_rapide()
  * @covers saisies_determiner_recul_rapide()
+ * @covers saisies_resumer_etapes_futures()
+ * @covers saisies_determiner_options_demandees_resumer_etapes_futures()
  * @uses saisies_lister_champs()
  * @uses saisies_lister_par_nom()
  * @uses saisies_lister_par_etapes()
@@ -208,4 +210,208 @@ class FormulaireTest extends TestCase {
 			$this->assertTrue(True);
 		}
 	}
+
+	static public function dataResumerEtapesFutures(): array {
+		return [
+			'avec_titre' => [
+				// Expected
+				[
+						'etape_3' => [
+							'afficher_si' => '@toto@ == "hop"',
+							'label' => 'etape 3',
+						],
+						'etape_4' => [
+							'afficher_si' => 'false',
+							'label' => 'etape 4',
+						],
+						'etape_5' => [
+							'afficher_si' => 'true',
+							'label' => 'etape 5',
+						],
+				],
+				// Etapes
+				[
+					'etape_1' => [
+						'options' => [
+							'label' => 'etape 1',
+							'afficher_si' => 'false',
+						],
+						'saisies' => []
+					],
+					'etape_2' => [
+						'options' => [
+							'label' => 'etape 2',
+							'icone' => 'etape2.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => [
+							[
+								'saisie' => 'input',
+								'options' => [
+									'nom' => 'toto',
+								]
+							]
+						]
+					],
+					'etape_3' => [
+						'options' => [
+							'label' => 'etape 3',
+							'icone' => 'etape3.svg',
+							'taille_icone' => '16px',
+							'afficher_si' => '@toto@ == "hop"',
+						],
+						'saisies' => []
+					],
+					'etape_4' => [
+						'options' => [
+							'label' => 'etape 4',
+							'afficher_si' => 'false',
+							'icone' => 'etape4.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => []
+					],
+					'etape_5' => [
+						'options' => [
+							'label' => 'etape 5',
+							'afficher_si' => 'true',
+							'icone' => 'etape5.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => []
+					]
+				],
+				// Etape
+				2,
+				// Options demandées
+				[
+					'afficher_si',
+					'label'
+				]
+			],
+			'sans_titre' => [
+				// Expected
+				[
+						'etape_3' => [
+							'afficher_si' => '@toto@ == "hop"',
+						],
+						'etape_4' => [
+							'afficher_si' => 'false',
+						],
+						'etape_5' => [
+							'afficher_si' => 'true',
+						],
+				],
+				// Etapes
+				[
+					'etape_1' => [
+						'options' => [
+							'label' => 'etape 1',
+							'afficher_si' => 'false',
+						],
+						'saisies' => []
+					],
+					'etape_2' => [
+						'options' => [
+							'label' => 'etape 2',
+							'icone' => 'etape2.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => [
+							[
+								'saisie' => 'input',
+								'options' => [
+									'nom' => 'toto',
+								]
+							]
+						]
+					],
+					'etape_3' => [
+						'options' => [
+							'label' => 'etape 3',
+							'icone' => 'etape3.svg',
+							'taille_icone' => '16px',
+							'afficher_si' => '@toto@ == "hop"',
+						],
+						'saisies' => []
+					],
+					'etape_4' => [
+						'options' => [
+							'label' => 'etape 4',
+							'afficher_si' => 'false',
+							'icone' => 'etape4.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => []
+					],
+					'etape_5' => [
+						'options' => [
+							'label' => 'etape 5',
+							'afficher_si' => 'true',
+							'icone' => 'etape5.svg',
+							'taille_icone' => '16px'
+						],
+						'saisies' => []
+					]
+				],
+				// Etape
+				2,
+				// Options demandeées
+				[
+						'afficher_si'
+				]
+			]
+		];
+	}
+	/**
+	 * @dataProvider dataResumerEtapesFutures
+	 * @uses saisies_afficher_si_evaluer_plugin()
+	 * @uses saisies_afficher_si_secure()
+	 * @uses saisies_afficher_si_verifier_syntaxe()
+	 * @uses saisies_lister_par_nom()
+	 * @uses saisie_nom2name()
+	 * @uses saisies_afficher_si_js_defaut()
+	 * @uses saisies_evaluer_afficher_si()
+	**/
+	public function testResumerEtapesFutures(array $expected, array $etapes, int $etape, array $options_demandees): void {
+		$actual = saisies_resumer_etapes_futures($etapes, $etape, $options_demandees);
+		$this->assertSame($expected, $actual);
+	}
+
+	static function dataDeterminerOptionsDemandeesResumerEtapesFutures() {
+		return [
+			'standard' => [
+				// Expected
+				['afficher_si'],
+				// Provided
+				[]
+			],
+			'etapes_precedent_suivant_titrer_on' =>  [
+				// Expected
+				['afficher_si', 'label'],
+				// Provided
+				['etapes_precedent_suivant_titrer' => 'on']
+			],
+			'etapes_precedent_suivant_titrer_true' =>  [
+				// Expected
+				['afficher_si', 'label'],
+				// Provided
+				['etapes_precedent_suivant_titrer' => true]
+			],
+			'etapes_precedent_suivant_titrer_off' =>  [
+				// Expected
+				['afficher_si'],
+				// Provided
+				['etapes_precedent_suivant_titrer' => '']
+			],
+		];
+	}
+
+	/**
+	 @dataProvider dataDeterminerOptionsDemandeesResumerEtapesFutures
+	*/
+	public function testDeterminerOptionsDemandeesResumerEtapesFutures($expected, $provided) {
+		$actual = saisies_determiner_options_demandees_resumer_etapes_futures($provided);
+		$this->assertEquals($expected, $actual);
+	}
 }
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index a79ecba8..18dfb75b 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -314,6 +314,14 @@ if (!function_exists('find_all_in_path')) {
 	}
 }
 
+
+if (!function_exists('recuperer_fond')) {
+	// Pour récuperer fond, on envoi un tableau qui donne le nom et les args du fond
+	function recuperer_fond(string $nom, array $contexte): array{
+		return ['nom' => $nom, 'contexte' => $contexte];
+	}
+}
+
 /**
  * Mock pour la fonction de translitteration
  * Cette fonction n'est utilisé que pour des tri alphab accentué
-- 
GitLab