diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts')
-rw-r--r-- | src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts new file mode 100644 index 000000000..febbec163 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts @@ -0,0 +1,285 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; + +import _ from 'lodash'; +import { ToastrService } from 'ngx-toastr'; + +import { configureTestBed } from '~/testing/unit-test-helper'; +import { RbdService } from '../api/rbd.service'; +import { NotificationType } from '../enum/notification-type.enum'; +import { CdNotificationConfig } from '../models/cd-notification'; +import { FinishedTask } from '../models/finished-task'; +import { CdDatePipe } from '../pipes/cd-date.pipe'; +import { NotificationService } from './notification.service'; +import { TaskMessageService } from './task-message.service'; + +describe('NotificationService', () => { + let service: NotificationService; + const toastFakeService = { + error: () => true, + info: () => true, + success: () => true + }; + + configureTestBed({ + providers: [ + NotificationService, + TaskMessageService, + { provide: ToastrService, useValue: toastFakeService }, + { provide: CdDatePipe, useValue: { transform: (d: any) => d } }, + RbdService + ], + imports: [HttpClientTestingModule] + }); + + beforeEach(() => { + service = TestBed.inject(NotificationService); + service.removeAll(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should read empty notification list', () => { + localStorage.setItem('cdNotifications', '[]'); + expect(service['dataSource'].getValue()).toEqual([]); + }); + + it('should read old notifications', fakeAsync(() => { + localStorage.setItem( + 'cdNotifications', + '[{"type":2,"message":"foobar","timestamp":"2018-05-24T09:41:32.726Z"}]' + ); + service = new NotificationService(null, null, null); + expect(service['dataSource'].getValue().length).toBe(1); + })); + + it('should cancel a notification', fakeAsync(() => { + const timeoutId = service.show(NotificationType.error, 'Simple test'); + service.cancel(timeoutId); + tick(5000); + expect(service['dataSource'].getValue().length).toBe(0); + })); + + describe('Saved notifications', () => { + const expectSavedNotificationToHave = (expected: object) => { + tick(510); + expect(service['dataSource'].getValue().length).toBe(1); + const notification = service['dataSource'].getValue()[0]; + Object.keys(expected).forEach((key) => { + expect(notification[key]).toBe(expected[key]); + }); + }; + + const addNotifications = (quantity: number) => { + for (let index = 0; index < quantity; index++) { + service.show(NotificationType.info, `${index}`); + tick(510); + } + }; + + beforeEach(() => { + spyOn(service, 'show').and.callThrough(); + service.cancel((<any>service)['justShownTimeoutId']); + }); + + it('should create a success notification and save it', fakeAsync(() => { + service.show(new CdNotificationConfig(NotificationType.success, 'Simple test')); + expectSavedNotificationToHave({ type: NotificationType.success }); + })); + + it('should create an error notification and save it', fakeAsync(() => { + service.show(NotificationType.error, 'Simple test'); + expectSavedNotificationToHave({ type: NotificationType.error }); + })); + + it('should create an info notification and save it', fakeAsync(() => { + service.show(new CdNotificationConfig(NotificationType.info, 'Simple test')); + expectSavedNotificationToHave({ + type: NotificationType.info, + title: 'Simple test', + message: undefined + }); + })); + + it('should never have more then 10 notifications', fakeAsync(() => { + addNotifications(15); + expect(service['dataSource'].getValue().length).toBe(10); + })); + + it('should show a success task notification, but not save it', fakeAsync(() => { + const task = _.assign(new FinishedTask(), { + success: true + }); + + service.notifyTask(task, true); + tick(1500); + + expect(service.show).toHaveBeenCalled(); + const notifications = service['dataSource'].getValue(); + expect(notifications.length).toBe(0); + })); + + it('should be able to stop notifyTask from notifying', fakeAsync(() => { + const task = _.assign(new FinishedTask(), { + success: true + }); + const timeoutId = service.notifyTask(task, true); + service.cancel(timeoutId); + tick(100); + expect(service['dataSource'].getValue().length).toBe(0); + })); + + it('should show a error task notification', fakeAsync(() => { + const task = _.assign( + new FinishedTask('rbd/create', { + pool_name: 'somePool', + image_name: 'someImage' + }), + { + success: false, + exception: { + code: 17 + } + } + ); + service.notifyTask(task); + + tick(1500); + + expect(service.show).toHaveBeenCalled(); + const notifications = service['dataSource'].getValue(); + expect(notifications.length).toBe(0); + })); + + it('combines different notifications with the same title', fakeAsync(() => { + service.show(NotificationType.error, '502 - Bad Gateway', 'Error occurred in path a'); + tick(60); + service.show(NotificationType.error, '502 - Bad Gateway', 'Error occurred in path b'); + expectSavedNotificationToHave({ + type: NotificationType.error, + title: '502 - Bad Gateway', + message: '<ul><li>Error occurred in path a</li><li>Error occurred in path b</li></ul>' + }); + })); + + it('should remove a single notification', fakeAsync(() => { + addNotifications(5); + let messages = service['dataSource'].getValue().map((notification) => notification.title); + expect(messages).toEqual(['4', '3', '2', '1', '0']); + service.remove(2); + messages = service['dataSource'].getValue().map((notification) => notification.title); + expect(messages).toEqual(['4', '3', '1', '0']); + })); + + it('should remove all notifications', fakeAsync(() => { + addNotifications(5); + expect(service['dataSource'].getValue().length).toBe(5); + service.removeAll(); + expect(service['dataSource'].getValue().length).toBe(0); + })); + }); + + describe('notification queue', () => { + const n1 = new CdNotificationConfig(NotificationType.success, 'Some success'); + const n2 = new CdNotificationConfig(NotificationType.info, 'Some info'); + + const showArray = (arr: any[]) => arr.forEach((n) => service.show(n)); + + beforeEach(() => { + spyOn(service, 'save').and.stub(); + }); + + it('filters out duplicated notifications on single call', fakeAsync(() => { + showArray([n1, n1, n2, n2]); + tick(510); + expect(service.save).toHaveBeenCalledTimes(2); + })); + + it('filters out duplicated notifications presented in different calls', fakeAsync(() => { + showArray([n1, n2]); + showArray([n1, n2]); + tick(1000); + expect(service.save).toHaveBeenCalledTimes(2); + })); + + it('will reset the timeout on every call', fakeAsync(() => { + showArray([n1, n2]); + tick(490); + showArray([n1, n2]); + tick(450); + expect(service.save).toHaveBeenCalledTimes(0); + tick(60); + expect(service.save).toHaveBeenCalledTimes(2); + })); + + it('wont filter out duplicated notifications if timeout was reached before', fakeAsync(() => { + showArray([n1, n2]); + tick(510); + showArray([n1, n2]); + tick(510); + expect(service.save).toHaveBeenCalledTimes(4); + })); + }); + + describe('showToasty', () => { + let toastr: ToastrService; + const time = '2022-02-22T00:00:00.000Z'; + + beforeEach(() => { + const baseTime = new Date(time); + spyOn(global, 'Date').and.returnValue(baseTime); + spyOn(window, 'setTimeout').and.callFake((fn) => fn()); + + toastr = TestBed.inject(ToastrService); + // spyOn needs to know the methods before spying and can't read the array for clarification + ['error', 'info', 'success'].forEach((method: 'error' | 'info' | 'success') => + spyOn(toastr, method).and.stub() + ); + }); + + it('should show with only title defined', () => { + service.show(NotificationType.info, 'Some info'); + expect(toastr.info).toHaveBeenCalledWith( + `<small class="date">${time}</small>` + + '<i class="float-end custom-icon ceph-icon" title="Ceph"></i>', + 'Some info', + undefined + ); + }); + + it('should show with title and message defined', () => { + service.show( + () => + new CdNotificationConfig(NotificationType.error, 'Some error', 'Some operation failed') + ); + expect(toastr.error).toHaveBeenCalledWith( + 'Some operation failed<br>' + + `<small class="date">${time}</small>` + + '<i class="float-end custom-icon ceph-icon" title="Ceph"></i>', + 'Some error', + undefined + ); + }); + + it('should show with title, message and application defined', () => { + service.show( + new CdNotificationConfig( + NotificationType.success, + 'Alert resolved', + 'Some alert resolved', + undefined, + 'Prometheus' + ) + ); + expect(toastr.success).toHaveBeenCalledWith( + 'Some alert resolved<br>' + + `<small class="date">${time}</small>` + + '<i class="float-end custom-icon prometheus-icon" title="Prometheus"></i>', + 'Alert resolved', + undefined + ); + }); + }); +}); |