using MaBibli.Client.Services; namespace MaBibli.Tests; /// /// La flèche de retour du bandeau remonte d'un cran de route, et ne sort jamais de /// l'application. Ces cas sont ceux qui ont été écrits noir sur blanc avant de coder. /// public class RemonteeRoutesTests { [Theory] // Fiche livre [InlineData("/livres/3/edition", "/livres/3")] [InlineData("/livres/3", "/")] // Séries [InlineData("/series/7/edition", "/series/7")] [InlineData("/series/7/ordre", "/series/7")] [InlineData("/series/7", "/series")] [InlineData("/series", "/")] // Envies [InlineData("/souhaits/5/edition", "/souhaits")] [InlineData("/souhaits/revues/5/edition", "/souhaits")] [InlineData("/souhaits/ordre", "/souhaits")] [InlineData("/souhaits/ajout", "/souhaits")] [InlineData("/souhaits", "/")] // Ajout d'un ouvrage [InlineData("/ajout", "/")] [InlineData("/ajout/isbn", "/")] [InlineData("/ajout/manuel", "/")] // Revues [InlineData("/revues/ajout", "/revues")] [InlineData("/revues/12/edition", "/revues/12")] [InlineData("/revues/12", "/revues")] [InlineData("/revues", "/")] // Auteurs [InlineData("/auteurs/5/bibliographie", "/auteurs")] [InlineData("/auteurs", "/")] // Le reste du menu [InlineData("/prets", "/")] // L'annexe du menu [InlineData("/a-propos", "/")] [InlineData("/", "/")] public void Remonte_dun_cran(string chemin, string attendu) => Assert.Equal(attendu, RemonteeRoutes.Parent(chemin)); /// /// « ajout » n'est pas un identifiant : sans l'ordre de lecture de la table, il serait pris /// pour la fiche d'une revue. /// [Fact] public void Ajout_de_revue_nest_pas_une_fiche() => Assert.Equal("/revues", RemonteeRoutes.Parent("/revues/ajout")); /// La requête d'un filtre ou d'un tri ne change pas la parenté. [Theory] [InlineData("/?auteur=3", "/")] [InlineData("/livres/3/edition?onglet=pret", "/livres/3")] [InlineData("/auteurs/5/bibliographie#bas", "/auteurs")] public void La_requete_et_le_fragment_sont_ignores(string chemin, string attendu) => Assert.Equal(attendu, RemonteeRoutes.Parent(chemin)); /// Le chemin peut arriver relatif à la base, sans barre oblique de tête. [Theory] [InlineData("livres/3/edition", "/livres/3")] [InlineData("series/7/ordre", "/series/7")] [InlineData("", "/")] public void Un_chemin_relatif_est_accepte(string chemin, string attendu) => Assert.Equal(attendu, RemonteeRoutes.Parent(chemin)); /// Une barre oblique finale ne doit pas faire manquer la ligne de table. [Fact] public void La_barre_finale_est_ignoree() => Assert.Equal("/livres/3", RemonteeRoutes.Parent("/livres/3/edition/")); /// /// Repli d'une route inconnue : la destination de menu de sa branche, jamais un chemin /// deviné, et jamais rien d'externe. /// [Theory] [InlineData("/revues/12/inconnu", "/revues")] [InlineData("/series/7/inconnu", "/series")] [InlineData("/inconnu/total", "/")] [InlineData("/livres", "/")] public void Une_route_inconnue_retombe_sur_sa_branche(string chemin, string attendu) => Assert.Equal(attendu, RemonteeRoutes.Parent(chemin)); /// /// ⚠️ Le garde-fou du lot S : quoi qu'on lui donne, le retour reste dans l'application. /// Une URL absolue ou un chemin protocolaire ne doit jamais ressortir tel quel. /// [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] [InlineData("https://exemple.test/livres/3")] [InlineData("//exemple.test/")] [InlineData("/livres/abc/edition")] public void Ne_sort_jamais_de_lapplication(string? chemin) { var parent = RemonteeRoutes.Parent(chemin); Assert.StartsWith("/", parent); Assert.DoesNotContain("//", parent); Assert.DoesNotContain(":", parent); } }