import { TestBed } from '@angular/core/testing'; import { provideRouter, Router } from '@angular/router'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { UploadComponent } from './upload.component'; import { routes } from '../app.routes'; describe('UploadComponent', () => { let component: UploadComponent; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [UploadComponent], providers: [provideHttpClient(), provideHttpClientTesting(), provideRouter(routes)], }).compileComponents(); }); it('should normalise tag input: lowercases and splits on comma/space', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; component.tagInput = 'CAT, Funny reaction'; expect(component.parseTagInput(component.tagInput)).toEqual(['cat', 'funny', 'reaction']); }); it('should split on commas', () => { const fixture = TestBed.createComponent(UploadComponent); expect(fixture.componentInstance.parseTagInput('a,b,c')).toEqual(['a', 'b', 'c']); }); it('should filter empty tokens', () => { const fixture = TestBed.createComponent(UploadComponent); expect(fixture.componentInstance.parseTagInput(' ,, cat ,,')).toEqual(['cat']); }); it('on duplicate response: shows toast and navigates to detail', async () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; const router = TestBed.inject(Router); spyOn(router, 'navigate'); await component.handleUploadResponse({ id: 'abc', duplicate: true } as Parameters[0]); expect(component.toastMessage).toContain('library'); expect(router.navigate).toHaveBeenCalledWith(['/images', 'abc']); }); it('on success response: shows success toast and navigates to detail', async () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; const router = TestBed.inject(Router); spyOn(router, 'navigate'); await component.handleUploadResponse({ id: 'xyz', duplicate: false } as Parameters[0]); expect(component.toastMessage).toBeTruthy(); expect(router.navigate).toHaveBeenCalledWith(['/images', 'xyz']); }); it('on error response: shows inline error, no navigation', async () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; const router = TestBed.inject(Router); spyOn(router, 'navigate'); component.handleUploadError({ status: 422, error: { detail: 'bad file', code: 'invalid_mime_type' } }); expect(component.errorMessage).toBeTruthy(); expect(router.navigate).not.toHaveBeenCalled(); }); // New polish tests it('submit button is disabled when no file is selected', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; fixture.detectChanges(); const btn = (fixture.nativeElement as HTMLElement).querySelector('button[type="submit"]') as HTMLButtonElement; expect(btn.disabled).toBeTrue(); }); it('submit button shows "Uploading…" label while uploading is true', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; component.uploading = true; fixture.detectChanges(); const btn = (fixture.nativeElement as HTMLElement).querySelector('button[type="submit"]') as HTMLButtonElement; expect(btn.textContent).toContain('Uploading'); expect(btn.disabled).toBeTrue(); }); it('showSuccess banner is hidden initially', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; fixture.detectChanges(); expect((fixture.nativeElement as HTMLElement).querySelector('.success-banner')).toBeNull(); }); it('showSuccess banner is visible when showSuccess is true', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; component.showSuccess = true; component.uploadedFilename = 'photo.jpg'; fixture.detectChanges(); const banner = (fixture.nativeElement as HTMLElement).querySelector('.success-banner'); expect(banner).not.toBeNull(); expect(banner!.textContent).toContain('photo.jpg'); }); it('shows validation error message for 422 response', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; fixture.detectChanges(); component.handleUploadError({ status: 422, error: { detail: 'Unsupported file type', code: 'invalid_mime_type' } }); fixture.detectChanges(); const err = (fixture.nativeElement as HTMLElement).querySelector('.error'); expect(err!.textContent).toContain('Unsupported file type'); }); it('shows generic error message for network error', () => { const fixture = TestBed.createComponent(UploadComponent); component = fixture.componentInstance; fixture.detectChanges(); component.handleUploadError({ status: 500, error: null }); fixture.detectChanges(); const err = (fixture.nativeElement as HTMLElement).querySelector('.error'); expect(err!.textContent).toBeTruthy(); }); });