import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { of, throwError } from 'rxjs' import { OcrTemplate } from 'src/app/data/ocr-template' import { DirtyFormGuard } from 'src/app/guards/dirty-form.guard' import { PermissionsService } from 'src/app/services/permissions.service' import { DocumentTypeService } from 'src/app/services/rest/document-type.service' import { OcrTemplateService } from 'src/app/services/rest/ocr-template.service' import { ToastService } from 'src/app/services/toast.service' import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' import { OcrTemplateEditorComponent } from './ocr-template-editor/ocr-template-editor.component' import { OcrTemplatesComponent } from './ocr-templates.component' const templates: OcrTemplate[] = [ { id: 1, name: 'Invoices', document_type: 2, sample_document: null, source_width: 0, source_height: 0, enabled: true, zones: [], }, ] describe('OcrTemplatesComponent', () => { let component: OcrTemplatesComponent let fixture: ComponentFixture let ocrTemplateService: OcrTemplateService let modalService: NgbModal let toastService: ToastService let modal: NgbModalRef beforeEach(() => { TestBed.configureTestingModule({ imports: [NgxBootstrapIconsModule.pick(allIcons), OcrTemplatesComponent], providers: [ { provide: PermissionsService, useValue: { currentUserCan: () => true, currentUserHasObjectPermissions: () => true, currentUserOwnsObject: () => true, }, }, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ], }) ocrTemplateService = TestBed.inject(OcrTemplateService) jest .spyOn(ocrTemplateService, 'listAll') .mockReturnValue(of({ count: 1, all: [1], results: templates })) jest .spyOn(TestBed.inject(DocumentTypeService), 'listAll') .mockReturnValue( of({ count: 1, all: [2], results: [{ id: 2, name: 'Invoice' }] }) ) modalService = TestBed.inject(NgbModal) modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1])) toastService = TestBed.inject(ToastService) fixture = TestBed.createComponent(OcrTemplatesComponent) component = fixture.componentInstance fixture.detectChanges() }) afterEach(() => modalService.dismissAll()) it('should show templates with their document type', () => { const text = fixture.nativeElement.textContent expect(text).toContain('Invoices') expect(text).toContain('Invoice') }) it('should support create, show notification on error / success', () => { const toastErrorSpy = jest.spyOn(toastService, 'showError') const toastSuccessSpy = jest.spyOn(toastService, 'showSuccess') const reloadSpy = jest.spyOn(component, 'reload') fixture.debugElement .queryAll(By.css('pngx-page-header button')) .find((button) => button.nativeElement.textContent.includes('Add')) .triggerEventHandler('click') const editor = modal.componentInstance as OcrTemplateEditorComponent expect(editor).toBeInstanceOf(OcrTemplateEditorComponent) expect(editor.object).toBeUndefined() editor.failed.emit({ error: 'error creating template' }) expect(toastErrorSpy).toHaveBeenCalled() expect(reloadSpy).not.toHaveBeenCalled() editor.succeeded.emit(templates[0]) expect(toastSuccessSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalled() }) it('should pass the template to the editor', () => { component.editTemplate(templates[0]) expect(modal.componentInstance.object).toBe(templates[0]) }) it('should confirm closing the editor with unsaved changes', async () => { const confirmSpy = jest .spyOn(TestBed.inject(DirtyFormGuard), 'confirmChanges') .mockReturnValue(of(false)) component.editTemplate() const editor = modal.componentInstance as OcrTemplateEditorComponent editor.ngOnInit() modal.dismiss() await fixture.whenStable() expect(confirmSpy).not.toHaveBeenCalled() component.editTemplate() const dirtyEditor = modal.componentInstance as OcrTemplateEditorComponent dirtyEditor.ngOnInit() dirtyEditor.template.name = 'Changed' modal.dismiss() await fixture.whenStable() expect(confirmSpy).toHaveBeenCalled() expect(modalService.hasOpenModals()).toBe(true) dirtyEditor.saving.set(true) confirmSpy.mockClear() modal.dismiss() await fixture.whenStable() expect(confirmSpy).not.toHaveBeenCalled() expect(modalService.hasOpenModals()).toBe(true) }) it('should only send the enabled flag when toggling', () => { const patchSpy = jest .spyOn(ocrTemplateService, 'patch') .mockReturnValueOnce(throwError(() => new Error('error toggling'))) .mockReturnValueOnce(of(templates[0])) const toastErrorSpy = jest.spyOn(toastService, 'showError') const toastSuccessSpy = jest.spyOn(toastService, 'showSuccess') const reloadSpy = jest.spyOn(component, 'reload') const toggle = fixture.debugElement.query(By.css('.form-switch input')) toggle.nativeElement.click() expect(patchSpy).toHaveBeenCalledWith({ id: 1, enabled: false }) expect(toastErrorSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalledTimes(1) component.toggleTemplateEnabled(templates[0]) expect(toastSuccessSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalledTimes(2) }) it('should support delete, show notification on error / success', () => { const toastErrorSpy = jest.spyOn(toastService, 'showError') const toastSuccessSpy = jest.spyOn(toastService, 'showSuccess') const deleteSpy = jest.spyOn(ocrTemplateService, 'delete') const reloadSpy = jest.spyOn(component, 'reload') component.deleteTemplate(templates[0]) const dialog = modal.componentInstance as ConfirmDialogComponent deleteSpy.mockReturnValueOnce(throwError(() => new Error('error deleting'))) dialog.confirmClicked.emit() expect(toastErrorSpy).toHaveBeenCalled() expect(dialog.buttonsEnabled()).toBe(true) expect(reloadSpy).not.toHaveBeenCalled() deleteSpy.mockReturnValueOnce(of(true)) dialog.confirmClicked.emit() expect(toastSuccessSpy).toHaveBeenCalled() expect(reloadSpy).toHaveBeenCalled() }) })