MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using MaBibli.Client.Services;
|
||||
|
||||
namespace MaBibli.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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));
|
||||
|
||||
/// <summary>
|
||||
/// « ajout » n'est pas un identifiant : sans l'ordre de lecture de la table, il serait pris
|
||||
/// pour la fiche d'une revue.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Ajout_de_revue_nest_pas_une_fiche() =>
|
||||
Assert.Equal("/revues", RemonteeRoutes.Parent("/revues/ajout"));
|
||||
|
||||
/// <summary>La requête d'un filtre ou d'un tri ne change pas la parenté.</summary>
|
||||
[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));
|
||||
|
||||
/// <summary>Le chemin peut arriver relatif à la base, sans barre oblique de tête.</summary>
|
||||
[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));
|
||||
|
||||
/// <summary>Une barre oblique finale ne doit pas faire manquer la ligne de table.</summary>
|
||||
[Fact]
|
||||
public void La_barre_finale_est_ignoree() =>
|
||||
Assert.Equal("/livres/3", RemonteeRoutes.Parent("/livres/3/edition/"));
|
||||
|
||||
/// <summary>
|
||||
/// Repli d'une route inconnue : la destination de menu de sa branche, jamais un chemin
|
||||
/// deviné, et jamais rien d'externe.
|
||||
/// </summary>
|
||||
[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));
|
||||
|
||||
/// <summary>
|
||||
/// ⚠️ 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.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user