import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; import { of, throwError } from 'rxjs'; import { vi } from 'vitest'; import { KeycloakService } from '../auth/keycloak.service'; import { ProjectContextService } from '../projects/project-context.service'; import { ProjectEntity } from '../projects/projects.store'; import { VersionApiService } from '../version/version-api.service'; import { Menu } from './menu'; describe('Menu', () => { let component: Menu; let fixture: ComponentFixture; const isAuthenticated = signal(false); const username = signal(undefined); const projectId = signal(null); const project = signal(null); const keycloakMock = { isAuthenticated, username, logout: vi.fn(), login: vi.fn() }; const projectContextMock = { projectId, project }; const versionApiMock = { getVersion: vi.fn() }; beforeEach(async () => { isAuthenticated.set(false); projectId.set(null); project.set(null); keycloakMock.logout = vi.fn(); keycloakMock.login = vi.fn(); versionApiMock.getVersion = vi.fn(); await TestBed.configureTestingModule({ imports: [Menu], providers: [ provideRouter([]), { provide: KeycloakService, useValue: keycloakMock }, { provide: ProjectContextService, useValue: projectContextMock }, { provide: VersionApiService, useValue: versionApiMock }, ], }).compileComponents(); fixture = TestBed.createComponent(Menu); component = fixture.componentInstance; await fixture.whenStable(); }); it('should create', () => { expect(component).toBeTruthy(); }); describe('projectMenuItems', () => { it('returns empty array when no project is selected', () => { expect((component as any).projectMenuItems()).toEqual([]); }); it('returns dashboard, issues, milestones and statuts items when a project is selected', () => { projectId.set(5); const items = (component as any).projectMenuItems(); expect(items.length).toBe(4); expect(items[0].path).toBe('/projects/5/dashboard'); expect(items[1].path).toBe('/projects/5/issues'); expect(items[2].path).toBe('/projects/5/milestones'); expect(items[3].path).toBe('/projects/5/statuses'); }); }); it('logout calls keycloak.logout()', () => { (component as any).logout(); expect(keycloakMock.logout).toHaveBeenCalled(); }); it('shows Projets link when authenticated', async () => { isAuthenticated.set(true); fixture.detectChanges(); await fixture.whenStable(); const links = fixture.nativeElement.querySelectorAll('a.sidebar-link'); const hrefs = Array.from(links).map((l: any) => l.getAttribute('href') ?? ''); expect(hrefs.some((h) => h === '/projects')).toBe(true); }); describe('info button', () => { it('toggleInfo opens the popover and fetches API version', () => { versionApiMock.getVersion.mockReturnValue(of({ version: '1.2.3' })); (component as any).toggleInfo(); expect((component as any).showInfo()).toBe(true); expect((component as any).apiVersion()).toBe('1.2.3'); }); it('toggleInfo closes the popover when already open', () => { versionApiMock.getVersion.mockReturnValue(of({ version: '1.2.3' })); (component as any).toggleInfo(); (component as any).toggleInfo(); expect((component as any).showInfo()).toBe(false); }); it('does not call getVersion again when popover is reopened', () => { versionApiMock.getVersion.mockReturnValue(of({ version: '1.2.3' })); (component as any).toggleInfo(); (component as any).toggleInfo(); (component as any).toggleInfo(); expect(versionApiMock.getVersion).toHaveBeenCalledTimes(1); }); it('sets apiVersion to — on API error', () => { versionApiMock.getVersion.mockReturnValue(throwError(() => new Error('network'))); (component as any).toggleInfo(); expect((component as any).apiVersion()).toBe('—'); }); it('closeInfo hides the popover', () => { versionApiMock.getVersion.mockReturnValue(of({ version: '1.2.3' })); (component as any).toggleInfo(); (component as any).closeInfo(); expect((component as any).showInfo()).toBe(false); }); it('versionMismatch is false when versions match', () => { versionApiMock.getVersion.mockReturnValue(of({ version: (component as any).version })); (component as any).toggleInfo(); expect((component as any).versionMismatch()).toBe(false); }); it('versionMismatch is true when versions differ', () => { versionApiMock.getVersion.mockReturnValue(of({ version: '99.99.99' })); (component as any).toggleInfo(); expect((component as any).versionMismatch()).toBe(true); }); it('versionMismatch is false before API responds', () => { expect((component as any).versionMismatch()).toBe(false); }); }); });