Feat: Add Copy URL button and reusable toast notification system
Detail page now has a "Copy URL" button that copies the image's direct file URL to the clipboard. A toast service (BehaviorSubject-backed, auto-dismissing after 3s) confirms success or failure. ToastComponent is registered at the app root and available to all future features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
49
ui/src/app/toast/toast.component.spec.ts
Normal file
49
ui/src/app/toast/toast.component.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { ToastComponent } from './toast.component';
|
||||
import { ToastService } from '../services/toast.service';
|
||||
|
||||
describe('ToastComponent', () => {
|
||||
let toastSvc: jasmine.SpyObj<ToastService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
toastSvc = jasmine.createSpyObj('ToastService', [], { current$: of(null) });
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ToastComponent],
|
||||
providers: [{ provide: ToastService, useValue: toastSvc }],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('renders a .toast element with the correct message when current$ emits a toast', async () => {
|
||||
Object.defineProperty(toastSvc, 'current$', { value: of({ message: 'Done', type: 'success' as const }) });
|
||||
const fixture = TestBed.createComponent(ToastComponent);
|
||||
fixture.detectChanges();
|
||||
const el = (fixture.nativeElement as HTMLElement).querySelector('.toast');
|
||||
expect(el).not.toBeNull();
|
||||
expect(el?.textContent?.trim()).toBe('Done');
|
||||
});
|
||||
|
||||
it('adds the success CSS class when type is success', async () => {
|
||||
Object.defineProperty(toastSvc, 'current$', { value: of({ message: 'OK', type: 'success' as const }) });
|
||||
const fixture = TestBed.createComponent(ToastComponent);
|
||||
fixture.detectChanges();
|
||||
const el = (fixture.nativeElement as HTMLElement).querySelector('.toast');
|
||||
expect(el?.classList.contains('success')).toBeTrue();
|
||||
});
|
||||
|
||||
it('adds the error CSS class when type is error', async () => {
|
||||
Object.defineProperty(toastSvc, 'current$', { value: of({ message: 'Fail', type: 'error' as const }) });
|
||||
const fixture = TestBed.createComponent(ToastComponent);
|
||||
fixture.detectChanges();
|
||||
const el = (fixture.nativeElement as HTMLElement).querySelector('.toast');
|
||||
expect(el?.classList.contains('error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('renders nothing when current$ emits null', () => {
|
||||
const fixture = TestBed.createComponent(ToastComponent);
|
||||
fixture.detectChanges();
|
||||
const el = (fixture.nativeElement as HTMLElement).querySelector('.toast');
|
||||
expect(el).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user