Correction du bug de switch entre lore / campagne et la sidebar qui ne s'actualise pas en conséquence. Ajout d'un test playwright pour éviter toute régression à l'avenir
This commit is contained in:
77
web/e2e/tests/secondary-sidebar-isolation.spec.ts
Normal file
77
web/e2e/tests/secondary-sidebar-isolation.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { seedLoreWithFolder, deleteLore, type SeededLore } from '../fixtures/api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regression : la secondary sidebar fuyait entre sections.
|
||||||
|
*
|
||||||
|
* Bug initial (2026-05-19) : on est sur /lore/:id (la sidebar affiche l'arbre
|
||||||
|
* du Lore), on clique sur "Campagne" dans la sidebar principale → on arrive
|
||||||
|
* sur /campaigns, MAIS la sidebar secondaire continuait d'afficher l'arbre
|
||||||
|
* du Lore precedent.
|
||||||
|
*
|
||||||
|
* Cause : les composants top-level (campaigns.component, lore.component,
|
||||||
|
* game-systems.component, settings.component) ne nettoyaient pas la sidebar
|
||||||
|
* heritee d'une section precedente. Fix : appel a layoutService.hide() dans
|
||||||
|
* leur ngOnInit.
|
||||||
|
*/
|
||||||
|
test.describe('Secondary sidebar — isolation entre sections', () => {
|
||||||
|
let seededLore: SeededLore;
|
||||||
|
|
||||||
|
test.beforeEach(async ({ request }) => {
|
||||||
|
seededLore = await seedLoreWithFolder(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterEach(async ({ request }) => {
|
||||||
|
if (seededLore?.id) await deleteLore(request, seededLore.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lore detail → /campaigns : la sidebar secondaire disparait', async ({ page }) => {
|
||||||
|
// 1. Sur le detail d'un Lore, la sidebar secondaire est affichee avec
|
||||||
|
// le nom du Lore comme titre.
|
||||||
|
await page.goto(`/lore/${seededLore.id}`);
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toBeVisible();
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toContainText(seededLore.name);
|
||||||
|
|
||||||
|
// 2. Navigation vers la liste des campagnes (top-level).
|
||||||
|
await page.goto('/campaigns');
|
||||||
|
await expect(page.getByRole('heading', { name: /Vos Campagnes/i })).toBeVisible();
|
||||||
|
|
||||||
|
// 3. La sidebar secondaire ne doit PAS persister (sinon elle afficherait
|
||||||
|
// encore l'arbre du Lore precedent). Le *ngIf au niveau d'AppComponent
|
||||||
|
// la retire completement du DOM quand layoutService est en etat hidden.
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lore detail → /game-systems : la sidebar secondaire disparait', async ({ page }) => {
|
||||||
|
await page.goto(`/lore/${seededLore.id}`);
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toBeVisible();
|
||||||
|
|
||||||
|
await page.goto('/game-systems');
|
||||||
|
await expect(page.getByRole('heading', { name: /Systèmes de JDR/i })).toBeVisible();
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lore detail → /settings : la sidebar secondaire disparait', async ({ page }) => {
|
||||||
|
await page.goto(`/lore/${seededLore.id}`);
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toBeVisible();
|
||||||
|
|
||||||
|
await page.goto('/settings');
|
||||||
|
// Settings n'a pas de h1 forcement evident, on se base sur l'URL + l'absence
|
||||||
|
// de sidebar secondaire (objet du test).
|
||||||
|
await expect(page).toHaveURL(/\/settings$/);
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lore detail → /lore (liste racine) : la sidebar secondaire disparait', async ({ page }) => {
|
||||||
|
// Sur le detail, la sidebar est visible
|
||||||
|
await page.goto(`/lore/${seededLore.id}`);
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toBeVisible();
|
||||||
|
|
||||||
|
// Retour a la liste racine du Lore
|
||||||
|
await page.goto('/lore');
|
||||||
|
await expect(page.getByRole('heading', { name: /Vos Univers/i })).toBeVisible();
|
||||||
|
|
||||||
|
// La sidebar ne doit plus apparaitre sur la liste racine.
|
||||||
|
await expect(page.locator('app-secondary-sidebar')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LucideAngularModule, Map, Plus } from 'lucide-angular';
|
import { LucideAngularModule, Map, Plus } from 'lucide-angular';
|
||||||
import { CampaignService } from '../services/campaign.service';
|
import { CampaignService } from '../services/campaign.service';
|
||||||
|
import { LayoutService } from '../services/layout.service';
|
||||||
import { Campaign } from '../services/campaign.model';
|
import { Campaign } from '../services/campaign.model';
|
||||||
import { CampaignCreateComponent, CampaignCreatePayload } from './campaign/campaign-create/campaign-create.component';
|
import { CampaignCreateComponent, CampaignCreatePayload } from './campaign/campaign-create/campaign-create.component';
|
||||||
|
|
||||||
@@ -22,10 +23,15 @@ export class CampaignsComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private campaignService: CampaignService
|
private campaignService: CampaignService,
|
||||||
|
private layoutService: LayoutService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Liste racine de la section Campagnes : aucune sidebar secondaire ne
|
||||||
|
// doit subsister (ex: si on arrive depuis une page Lore qui en affichait
|
||||||
|
// une, elle persisterait sans ce hide() — cf. bug rapporte 2026-05-19).
|
||||||
|
this.layoutService.hide();
|
||||||
this.loadCampaigns();
|
this.loadCampaigns();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LucideAngularModule, Dices, Plus, Pencil, Trash2 } from 'lucide-angular';
|
import { LucideAngularModule, Dices, Plus, Pencil, Trash2 } from 'lucide-angular';
|
||||||
import { GameSystemService } from '../services/game-system.service';
|
import { GameSystemService } from '../services/game-system.service';
|
||||||
|
import { LayoutService } from '../services/layout.service';
|
||||||
import { GameSystem } from '../services/game-system.model';
|
import { GameSystem } from '../services/game-system.model';
|
||||||
import { ConfirmDialogService } from '../shared/confirm-dialog/confirm-dialog.service';
|
import { ConfirmDialogService } from '../shared/confirm-dialog/confirm-dialog.service';
|
||||||
|
|
||||||
@@ -24,10 +25,14 @@ export class GameSystemsComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private gameSystemService: GameSystemService,
|
private gameSystemService: GameSystemService,
|
||||||
private confirmDialog: ConfirmDialogService
|
private confirmDialog: ConfirmDialogService,
|
||||||
|
private layoutService: LayoutService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Page racine : on s'assure de ne pas heriter de la sidebar d'une
|
||||||
|
// section precedente (cf. fix CampaignsComponent / LoreComponent).
|
||||||
|
this.layoutService.hide();
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LucideAngularModule, BookOpen, Folder, Plus } from 'lucide-angular';
|
import { LucideAngularModule, BookOpen, Folder, Plus } from 'lucide-angular';
|
||||||
import { LoreService } from '../services/lore.service';
|
import { LoreService } from '../services/lore.service';
|
||||||
|
import { LayoutService } from '../services/layout.service';
|
||||||
import { Lore } from '../services/lore.model';
|
import { Lore } from '../services/lore.model';
|
||||||
import { LoreCreateComponent } from './lore-create/lore-create.component';
|
import { LoreCreateComponent } from './lore-create/lore-create.component';
|
||||||
|
|
||||||
@@ -26,10 +27,15 @@ export class LoreComponent implements OnInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private loreService: LoreService,
|
private loreService: LoreService,
|
||||||
|
private layoutService: LayoutService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Liste racine de la section Lore : aucune sidebar secondaire ne doit
|
||||||
|
// subsister (sinon elle persiste depuis la section precedente — bug
|
||||||
|
// symetrique a celui de CampaignsComponent).
|
||||||
|
this.layoutService.hide();
|
||||||
this.loadLores();
|
this.loadLores();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { LucideAngularModule, ArrowLeft, RefreshCw, Save, Check, AlertCircle, Do
|
|||||||
import { SettingsService, AppSettings, AppSettingsUpdate, OneMinModelGroup, OllamaPullEvent } from '../services/settings.service';
|
import { SettingsService, AppSettings, AppSettingsUpdate, OneMinModelGroup, OllamaPullEvent } from '../services/settings.service';
|
||||||
import { UpdatesService, UpdateStatus } from '../services/updates.service';
|
import { UpdatesService, UpdateStatus } from '../services/updates.service';
|
||||||
import { ConfigService } from '../services/config.service';
|
import { ConfigService } from '../services/config.service';
|
||||||
|
import { LayoutService } from '../services/layout.service';
|
||||||
import { LicenseService, LicenseStatusDTO, BetaStatusDTO, ChannelStatusDTO, ChannelName } from '../services/license.service';
|
import { LicenseService, LicenseStatusDTO, BetaStatusDTO, ChannelStatusDTO, ChannelName } from '../services/license.service';
|
||||||
import { ConfirmDialogService } from '../shared/confirm-dialog/confirm-dialog.service';
|
import { ConfirmDialogService } from '../shared/confirm-dialog/confirm-dialog.service';
|
||||||
|
|
||||||
@@ -133,10 +134,14 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
private updatesService: UpdatesService,
|
private updatesService: UpdatesService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private licenseService: LicenseService,
|
private licenseService: LicenseService,
|
||||||
private confirmDialog: ConfirmDialogService
|
private confirmDialog: ConfirmDialogService,
|
||||||
|
private layoutService: LayoutService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Page racine : on s'assure de ne pas heriter de la sidebar d'une
|
||||||
|
// section precedente (cf. fix CampaignsComponent / LoreComponent).
|
||||||
|
this.layoutService.hide();
|
||||||
this.loadSettings();
|
this.loadSettings();
|
||||||
if (this.config.updateCheckEnabled) {
|
if (this.config.updateCheckEnabled) {
|
||||||
this.checkUpdates();
|
this.checkUpdates();
|
||||||
|
|||||||
Reference in New Issue
Block a user