Replie les blocs de série au catalogue, cycle par cycle
Le catalogue ne rangeait un livre que sous sa série directe : il n'y avait qu'un seul niveau, donc rien à replier « à chaque niveau ». Le regroupement devient un arbre — un cycle coiffe ses séries, comme sur /series — et chaque nœud porte sa bascule. Tout reste déplié par défaut ; un bloc replié annonce ce qu'il contient, et l'état de repli appartient à l'écran, sans quoi une frappe dans la recherche le perdrait. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,10 +23,17 @@ public class GroupementCatalogueTests
|
||||
};
|
||||
|
||||
private static SerieDto Serie(int id, string titre, params (int Position, int LivreId)[] tomes) =>
|
||||
SousSerie(id, titre, null, 0, tomes);
|
||||
|
||||
private static SerieDto SousSerie(
|
||||
int id, string titre, int? parenteId, int position,
|
||||
params (int Position, int LivreId)[] tomes) =>
|
||||
new()
|
||||
{
|
||||
Id = id,
|
||||
Titre = titre,
|
||||
SerieParenteId = parenteId,
|
||||
Position = position,
|
||||
Elements =
|
||||
[
|
||||
.. tomes.Select(t => new ElementSerieDto
|
||||
@@ -133,4 +140,110 @@ public class GroupementCatalogueTests
|
||||
|
||||
Assert.Equal(["L'Éclat de cristal"], Assert.Single(entrees).Livres.Select(l => l.Titre));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ⚠️ Le cas du 2026-08-22 : un cycle est une série de séries, et le catalogue doit le
|
||||
/// montrer — sans quoi « replier chaque niveau » n'aurait qu'un seul niveau à replier.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Un_cycle_coiffe_les_series_qu_il_contient()
|
||||
{
|
||||
List<LivreDto> catalogue = [Livre(1, "Terre natale"), Livre(2, "L'Éclat de cristal")];
|
||||
|
||||
List<SerieDto> series =
|
||||
[
|
||||
Serie(1, "La Légende de Drizzt"),
|
||||
SousSerie(3, "La Trilogie des Elfes noirs", 1, 1, (0, 2)),
|
||||
SousSerie(2, "L'Elfe noir", 1, 0, (0, 1)),
|
||||
];
|
||||
|
||||
var cycle = Assert.Single(GroupementCatalogue.Grouper(catalogue, series));
|
||||
|
||||
Assert.Equal("La Légende de Drizzt", cycle.Serie!.Titre);
|
||||
Assert.Empty(cycle.Livres);
|
||||
Assert.Equal(2, cycle.NombreLivres);
|
||||
|
||||
// Les sous-séries suivent leur ordre de LECTURE dans le cycle, pas l'alphabet.
|
||||
Assert.Collection(
|
||||
cycle.SousEntrees,
|
||||
e =>
|
||||
{
|
||||
Assert.Equal("L'Elfe noir", e.Serie!.Titre);
|
||||
Assert.Equal(["Terre natale"], e.Livres.Select(l => l.Titre));
|
||||
},
|
||||
e =>
|
||||
{
|
||||
Assert.Equal("La Trilogie des Elfes noirs", e.Serie!.Titre);
|
||||
Assert.Equal(["L'Éclat de cristal"], e.Livres.Select(l => l.Titre));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Le bloc du cycle se pose là où tombe son premier tome, comme celui d'une série simple :
|
||||
/// l'ordre général du catalogue ne bouge pas.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Le_cycle_prend_la_place_du_premier_tome_de_sa_descendance()
|
||||
{
|
||||
List<LivreDto> catalogue =
|
||||
[
|
||||
Livre(1, "Dracula"),
|
||||
Livre(2, "Terre natale"),
|
||||
Livre(3, "Dune"),
|
||||
Livre(4, "L'Éclat de cristal"),
|
||||
];
|
||||
|
||||
List<SerieDto> series =
|
||||
[
|
||||
Serie(1, "Drizzt"),
|
||||
SousSerie(2, "L'Elfe noir", 1, 0, (0, 2)),
|
||||
SousSerie(3, "Les Elfes noirs", 1, 1, (0, 4)),
|
||||
];
|
||||
|
||||
Assert.Collection(
|
||||
GroupementCatalogue.Grouper(catalogue, series),
|
||||
e => Assert.Equal("Dracula", Assert.Single(e.Livres).Titre),
|
||||
e => Assert.Equal("Drizzt", e.Serie!.Titre),
|
||||
e => Assert.Equal("Dune", Assert.Single(e.Livres).Titre));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ⚠️ Un niveau qui ne porte rien n'existe pas : ce serait une indentation qui ne se
|
||||
/// replierait sur rien, et un titre de série dont aucun tome n'est au catalogue.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Les_series_sans_tome_visible_ne_font_pas_de_niveau()
|
||||
{
|
||||
List<SerieDto> series =
|
||||
[
|
||||
Serie(1, "Cycle"),
|
||||
SousSerie(2, "Vide", 1, 0, (0, 99)),
|
||||
SousSerie(3, "Garnie", 1, 1, (0, 5)),
|
||||
];
|
||||
|
||||
var cycle = Assert.Single(GroupementCatalogue.Grouper([Livre(5, "Un tome")], series));
|
||||
|
||||
Assert.Equal("Garnie", Assert.Single(cycle.SousEntrees).Serie!.Titre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Une parenté qui boucle en base ne doit pas faire tourner le regroupement sans fin :
|
||||
/// même garde-fou que la remontée du serveur, bornée par le nombre de séries.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Une_parente_qui_boucle_ne_bloque_pas_le_catalogue()
|
||||
{
|
||||
List<SerieDto> series =
|
||||
[
|
||||
SousSerie(1, "A", 2, 0, (0, 1)),
|
||||
SousSerie(2, "B", 1, 0),
|
||||
];
|
||||
|
||||
var entrees = GroupementCatalogue.Grouper([Livre(1, "Un livre")], series);
|
||||
|
||||
Assert.Equal([1], entrees.SelectMany(EntreesEtDescendance).SelectMany(e => e.Livres).Select(l => l.Id));
|
||||
}
|
||||
|
||||
private static IEnumerable<EntreeCatalogue> EntreesEtDescendance(EntreeCatalogue entree) =>
|
||||
[entree, .. entree.SousEntrees.SelectMany(EntreesEtDescendance)];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user