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:
51
ui/src/app/services/toast.service.spec.ts
Normal file
51
ui/src/app/services/toast.service.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Toast, ToastService } from './toast.service';
|
||||
|
||||
describe('ToastService', () => {
|
||||
let service: ToastService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ToastService);
|
||||
});
|
||||
|
||||
it('show() emits a toast with correct message and type', (done) => {
|
||||
service.current$.subscribe((toast) => {
|
||||
if (toast) {
|
||||
expect(toast.message).toBe('Hello!');
|
||||
expect(toast.type).toBe('error');
|
||||
done();
|
||||
}
|
||||
});
|
||||
service.show('Hello!', 'error');
|
||||
});
|
||||
|
||||
it('type defaults to success when not provided', (done) => {
|
||||
service.current$.subscribe((toast) => {
|
||||
if (toast) {
|
||||
expect(toast.type).toBe('success');
|
||||
done();
|
||||
}
|
||||
});
|
||||
service.show('Default type');
|
||||
});
|
||||
|
||||
it('current$ emits null after the duration elapses', fakeAsync(() => {
|
||||
const emitted: (string | null)[] = [];
|
||||
service.current$.subscribe((t: Toast | null) => emitted.push(t ? t.message : null));
|
||||
service.show('Auto-dismiss', 'success', 500);
|
||||
tick(500);
|
||||
expect(emitted).toContain(null);
|
||||
}));
|
||||
|
||||
it('calling show() again before timer fires replaces the active toast', fakeAsync(() => {
|
||||
const messages: (string | null)[] = [];
|
||||
service.current$.subscribe((t: Toast | null) => messages.push(t ? t.message : null));
|
||||
service.show('First', 'success', 1000);
|
||||
tick(200);
|
||||
service.show('Second', 'success', 1000);
|
||||
tick(0);
|
||||
const nonNull = messages.filter((m) => m !== null);
|
||||
expect(nonNull[nonNull.length - 1]).toBe('Second');
|
||||
}));
|
||||
});
|
||||
Reference in New Issue
Block a user