diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index 11a40d98d..a922754f8 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -409,4 +409,13 @@ describe('MailComponent', () => { jest.advanceTimersByTime(200) expect(editSpy).toHaveBeenCalled() }) + + it('should open processed mails dialog', () => { + completeSetup() + let modal: NgbModalRef + modalService.activeInstances.subscribe((refs) => (modal = refs[0])) + component.viewProcessedMails(mailRules[0] as MailRule) + const dialog = modal.componentInstance as any + expect(dialog.rule).toEqual(mailRules[0]) + }) }) diff --git a/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.spec.ts b/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.spec.ts index e69de29bb..ebc4e0006 100644 --- a/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.spec.ts @@ -0,0 +1,174 @@ +import { DatePipe } from '@angular/common' +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { + HttpTestingController, + provideHttpClientTesting, +} from '@angular/common/http/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { FormsModule } from '@angular/forms' +import { By } from '@angular/platform-browser' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { ToastService } from 'src/app/services/toast.service' +import { environment } from 'src/environments/environment' +import { ProcessedMailsDialogComponent } from './processed-mails-dialog.component' + +describe('ProcessedMailsDialogComponent', () => { + let component: ProcessedMailsDialogComponent + let fixture: ComponentFixture + let httpTestingController: HttpTestingController + let toastService: ToastService + + const rule: any = { id: 10, name: 'Mail Rule' } // minimal rule object for tests + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + ProcessedMailsDialogComponent, + FormsModule, + NgxBootstrapIconsModule.pick(allIcons), + ], + providers: [ + DatePipe, + NgbActiveModal, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }).compileComponents() + + httpTestingController = TestBed.inject(HttpTestingController) + toastService = TestBed.inject(ToastService) + fixture = TestBed.createComponent(ProcessedMailsDialogComponent) + component = fixture.componentInstance + component.rule = rule + }) + + afterEach(() => { + httpTestingController.verify() + }) + + function expectListRequest(ruleId: number) { + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}processed_mail/?page=1&page_size=50&ordering=-processed_at&rule=${ruleId}` + ) + expect(req.request.method).toEqual('GET') + return req + } + + it('should load processed mails on init', () => { + fixture.detectChanges() + const req = expectListRequest(rule.id) + const mails = [ + { + id: 1, + rule: rule.id, + folder: 'INBOX', + uid: 111, + subject: 'Subject 1', + received: new Date().toISOString(), + processed: new Date().toISOString(), + status: 'SUCCESS', + error: null, + }, + { + id: 2, + rule: rule.id, + folder: 'INBOX', + uid: 222, + subject: 'Subject 2', + received: new Date().toISOString(), + processed: new Date().toISOString(), + status: 'FAILED', + error: 'Something went wrong', + }, + ] + req.flush({ count: 2, results: mails }) + expect(component.loading).toBeFalsy() + expect(component.processedMails).toEqual(mails) + }) + + it('should delete selected mails and reload', () => { + fixture.detectChanges() + // initial load + const initialReq = expectListRequest(rule.id) + initialReq.flush({ count: 0, results: [] }) + + // select a couple of mails and delete + component.selectedMailIds.add(5) + component.selectedMailIds.add(6) + const toastInfoSpy = jest.spyOn(toastService, 'showInfo') + component.deleteSelected() + + const delReq = httpTestingController.expectOne( + `${environment.apiBaseUrl}processed_mail/bulk_delete/` + ) + expect(delReq.request.method).toEqual('POST') + expect(delReq.request.body).toEqual({ mail_ids: [5, 6] }) + delReq.flush({}) + + // reload after delete + const reloadReq = expectListRequest(rule.id) + reloadReq.flush({ count: 0, results: [] }) + expect(toastInfoSpy).toHaveBeenCalled() + }) + + it('should toggle all, toggle selected, and clear selection', () => { + fixture.detectChanges() + // initial load with two mails + const req = expectListRequest(rule.id) + const mails = [ + { + id: 1, + rule: rule.id, + folder: 'INBOX', + uid: 111, + subject: 'A', + received: new Date().toISOString(), + processed: new Date().toISOString(), + status: 'SUCCESS', + error: null, + }, + { + id: 2, + rule: rule.id, + folder: 'INBOX', + uid: 222, + subject: 'B', + received: new Date().toISOString(), + processed: new Date().toISOString(), + status: 'FAILED', + error: 'Oops', + }, + ] + req.flush({ count: 2, results: mails }) + fixture.detectChanges() + + // toggle all via header checkbox + const inputs = fixture.debugElement.queryAll( + By.css('input.form-check-input') + ) + const header = inputs[0].nativeElement as HTMLInputElement + header.dispatchEvent(new Event('click')) + header.checked = true + header.dispatchEvent(new Event('click')) + expect(component.selectedMailIds.size).toEqual(mails.length) + + // toggle a single mail + component.toggleSelected(mails[0] as any) + expect(component.selectedMailIds.has(mails[0].id)).toBeFalsy() + component.toggleSelected(mails[0] as any) + expect(component.selectedMailIds.has(mails[0].id)).toBeTruthy() + + // clear selection + component.clearSelection() + expect(component.selectedMailIds.size).toEqual(0) + expect(component.toggleAllEnabled).toBeFalsy() + }) + + it('should close the dialog', () => { + const activeModal = TestBed.inject(NgbActiveModal) + const closeSpy = jest.spyOn(activeModal, 'close') + component.close() + expect(closeSpy).toHaveBeenCalled() + }) +}) diff --git a/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.ts b/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.ts index 2acda5137..e595d67b1 100644 --- a/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.ts +++ b/src-ui/src/app/components/manage/mail/processed-mails-dialog/processed-mails-dialog.component.ts @@ -1,5 +1,6 @@ import { SlicePipe } from '@angular/common' import { Component, inject, Input } from '@angular/core' +import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal, NgbPagination, @@ -21,6 +22,8 @@ import { ToastService } from 'src/app/services/toast.service' NgbPagination, NgbTooltipModule, NgxBootstrapIconsModule, + FormsModule, + ReactiveFormsModule, SlicePipe, ], templateUrl: './processed-mails-dialog.component.html', diff --git a/src-ui/src/app/services/rest/processed-mail.service.spec.ts b/src-ui/src/app/services/rest/processed-mail.service.spec.ts index e69de29bb..a424c2cbb 100644 --- a/src-ui/src/app/services/rest/processed-mail.service.spec.ts +++ b/src-ui/src/app/services/rest/processed-mail.service.spec.ts @@ -0,0 +1,39 @@ +import { HttpTestingController } from '@angular/common/http/testing' +import { TestBed } from '@angular/core/testing' +import { Subscription } from 'rxjs' +import { environment } from 'src/environments/environment' +import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec' +import { ProcessedMailService } from './processed-mail.service' + +let httpTestingController: HttpTestingController +let service: ProcessedMailService +let subscription: Subscription +const endpoint = 'processed_mail' + +// run common tests +commonAbstractPaperlessServiceTests(endpoint, ProcessedMailService) + +describe('Additional service tests for ProcessedMailService', () => { + beforeEach(() => { + // Dont need to setup again + + httpTestingController = TestBed.inject(HttpTestingController) + service = TestBed.inject(ProcessedMailService) + }) + + afterEach(() => { + subscription?.unsubscribe() + httpTestingController.verify() + }) + + it('should call appropriate api endpoint for bulk delete', () => { + const ids = [1, 2, 3] + subscription = service.bulk_delete(ids).subscribe() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/bulk_delete/` + ) + expect(req.request.method).toEqual('POST') + expect(req.request.body).toEqual({ mail_ids: ids }) + req.flush({}) + }) +})