import { signal, WritableSignal } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { Router, provideRouter } from '@angular/router'; import { CharacterResponse } from './core/api/game-api.models'; import { WorldStore } from './features/world/world.store'; import { AppShellComponent } from './layout/app-shell/app-shell.component'; import { routes } from './app.routes'; describe('App', () => { let character: WritableSignal; let currentLocation: WritableSignal; let selectedConnection: WritableSignal; beforeEach(async () => { character = signal(null); currentLocation = signal(null); selectedConnection = signal(null); await TestBed.configureTestingModule({ imports: [AppShellComponent], providers: [ provideRouter([ { path: 'world', children: [] }, { path: 'hunt', children: [] }, ]), { provide: WorldStore, useValue: { character, currentLocation, selectedConnection }, }, ], }).compileComponents(); }); it('renders the reusable game shell with Karte and Jagd available', async () => { const fixture = TestBed.createComponent(AppShellComponent); const router = TestBed.inject(Router); await router.navigateByUrl('/world'); fixture.detectChanges(); await fixture.whenStable(); const element = fixture.nativeElement as HTMLElement; expect(element.querySelector('app-top-bar')).not.toBeNull(); expect(element.querySelector('app-side-navigation')).not.toBeNull(); expect(element.querySelector('main')).not.toBeNull(); expect(element.querySelector('app-game-footer')).not.toBeNull(); const mapButton = element.querySelector('[data-navigation="world"]'); expect(mapButton).not.toBeNull(); expect(mapButton?.disabled).toBe(false); expect(mapButton?.getAttribute('aria-current')).toBe('page'); expect(mapButton?.getAttribute('aria-label')).toBe('Karte'); const huntButton = element.querySelector('[data-navigation="hunt"]'); expect(huntButton).not.toBeNull(); expect(huntButton?.disabled).toBe(false); expect(huntButton?.getAttribute('aria-current')).toBeNull(); expect(huntButton?.getAttribute('aria-label')).toBe('Jagd'); for (const destination of ['quests', 'inventory', 'character']) { expect( element.querySelector(`[data-navigation="${destination}"]`)?.disabled, ).toBe(true); } expect(element.textContent).not.toContain('Shop'); }); it('marks Jagd as the active navigation entry while on /hunt', async () => { const fixture = TestBed.createComponent(AppShellComponent); const router = TestBed.inject(Router); await router.navigateByUrl('/hunt'); fixture.detectChanges(); await fixture.whenStable(); const element = fixture.nativeElement as HTMLElement; const mapButton = element.querySelector('[data-navigation="world"]'); const huntButton = element.querySelector('[data-navigation="hunt"]'); expect(huntButton?.getAttribute('aria-current')).toBe('page'); expect(mapButton?.getAttribute('aria-current')).toBeNull(); }); it('renders loaded character values supplied by the WorldStore', () => { character.set({ id: 'character-id', name: 'Mara Ashfall', level: 7, experience: 320, silver: 150, currentHp: 52, maxHp: 80, attack: 12, currentLocation: { id: 'location-id', key: 'south-gate', name: 'Südtor von Graufurt' }, }); const fixture = TestBed.createComponent(AppShellComponent); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain( 'Mara Ashfall', ); expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('Stufe 7'); expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('52 / 80'); expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('150'); expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('320'); }); it('redirects root and unknown routes to the world shell', () => { expect(routes.find((route) => route.path === '')).toMatchObject({ pathMatch: 'full', redirectTo: 'world', }); expect(routes.find((route) => route.path === '**')).toMatchObject({ redirectTo: 'world' }); }); });