frontend unit tests

toasts component testing

conditional import of angular setup-jest for vscode-jest support

Update jest.config.js

Create open-documents.service.spec.ts

Add unit tests for all REST services

settings service test

Remove component from settings service test

Create permissions.service.spec.ts

upload documents service tests

Update package.json

Create toast.service.spec.ts

Tasks service test

Statistics widget component tests

Update permissions.service.ts

Create app.component.spec.ts

settings component testing

tasks component unit testing

Management list component generic tests

Some management component tests

document notes component unit tests

Create document-list.component.spec.ts

Create save-view-config-dialog.component.spec.ts

Create filter-editor.component.spec.ts

small and large document cards unit testing

Create bulk-editor.component.spec.ts

document detail unit tests

saving work on documentdetail component spec

Create document-asn.component.spec.ts

dashboard & widgets unit testing

Fix ResizeObserver mock

common component unit tests

fix some merge errors

Update app-frame.component.spec.ts

Create page-header.component.spec.ts

input component unit tests

FilterableDropdownComponent unit testing

and found minor errors

update taskservice unit tests

Edit dialogs unit tests

Create date-dropdown.component.spec.ts

Remove selectors from guard tests

confirm dialog component tests

app frame component test

Miscellaneous component tests

Update document-list-view.service.spec.ts

directives unit tests

Remove unused resizeobserver mock

guard unit tests

Update query-params.spec.ts

try to fix flaky playwright

filter rules utils & testing

Interceptor unit tests

Pipes unit testing

Utils unit tests

Update upload-documents.service.spec.ts

consumer status service tests

Update setup-jest.ts

Create document-list-view.service.spec.ts

Update app-routing.module.ts
This commit is contained in:
shamoon
2023-05-23 15:02:54 -07:00
parent e329f6cdf1
commit 06def8c11e
145 changed files with 14832 additions and 169 deletions

View File

@@ -0,0 +1,43 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ClearableBadgeComponent } from './clearable-badge.component'
describe('ClearableBadgeComponent', () => {
let component: ClearableBadgeComponent
let fixture: ComponentFixture<ClearableBadgeComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ClearableBadgeComponent],
}).compileComponents()
fixture = TestBed.createComponent(ClearableBadgeComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support selected', () => {
component.selected = true
expect(component.active).toBeTruthy()
})
it('should support numbered', () => {
component.number = 3
fixture.detectChanges()
expect(component.active).toBeTruthy()
expect((fixture.nativeElement as HTMLDivElement).textContent).toContain('3')
})
it('should support selected', () => {
let clearedResult
component.selected = true
fixture.detectChanges()
component.cleared.subscribe((clear) => {
clearedResult = clear
})
fixture.nativeElement
.querySelectorAll('button')[0]
.dispatchEvent(new MouseEvent('click'))
expect(clearedResult).toBeTruthy()
})
})

View File

@@ -0,0 +1,99 @@
import {
ComponentFixture,
TestBed,
discardPeriodicTasks,
fakeAsync,
tick,
} from '@angular/core/testing'
import { ConfirmDialogComponent } from './confirm-dialog.component'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { Subject } from 'rxjs'
describe('ConfirmDialogComponent', () => {
let component: ConfirmDialogComponent
let modal: NgbActiveModal
let fixture: ComponentFixture<ConfirmDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ConfirmDialogComponent, SafeHtmlPipe],
providers: [NgbActiveModal, SafeHtmlPipe],
imports: [],
}).compileComponents()
modal = TestBed.inject(NgbActiveModal)
fixture = TestBed.createComponent(ConfirmDialogComponent)
component = fixture.componentInstance
component.title = 'Confirm delete'
component.messageBold = 'Do you really want to delete document file.pdf?'
component.message =
'The files for this document will be deleted permanently. This operation cannot be undone.'
component.btnClass = 'btn-danger'
component.btnCaption = 'Delete document'
fixture.detectChanges()
})
it('should support alternative', () => {
let alternativeClickedResult
let alternativeSubjectResult
component.alternativeClicked.subscribe((result) => {
alternativeClickedResult = true
})
component.alternative()
// with subject
const subject = new Subject<boolean>()
component.alternativeSubject = subject
subject.asObservable().subscribe((result) => {
alternativeSubjectResult = result
})
component.alternative()
expect(alternativeClickedResult).toBeTruthy()
expect(alternativeSubjectResult).toBeTruthy()
})
it('should support confirm', () => {
let confirmClickedResult
let confirmSubjectResult
component.confirmClicked.subscribe((result) => {
confirmClickedResult = true
})
component.confirm()
// with subject
const subject = new Subject<boolean>()
component.confirmSubject = subject
subject.asObservable().subscribe((result) => {
confirmSubjectResult = result
})
component.confirm()
expect(confirmClickedResult).toBeTruthy()
expect(confirmSubjectResult).toBeTruthy()
})
it('should support cancel & close modal', () => {
let confirmSubjectResult
const closeModalSpy = jest.spyOn(modal, 'close')
component.cancel()
const subject = new Subject<boolean>()
component.confirmSubject = subject
subject.asObservable().subscribe((result) => {
confirmSubjectResult = result
})
component.cancel()
// with subject
expect(closeModalSpy).toHaveBeenCalled()
expect(confirmSubjectResult).toBeFalsy()
})
it('should support delay confirm', fakeAsync(() => {
component.confirmButtonEnabled = false
component.delayConfirm(1)
expect(component.confirmButtonEnabled).toBeFalsy()
tick(1500)
fixture.detectChanges()
expect(component.confirmButtonEnabled).toBeTruthy()
discardPeriodicTasks()
}))
})

View File

@@ -0,0 +1,141 @@
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
let fixture: ComponentFixture<DateDropdownComponent>
import {
DateDropdownComponent,
DateSelection,
RelativeDate,
} from './date-dropdown.component'
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { SettingsService } from 'src/app/services/settings.service'
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DatePipe } from '@angular/common'
describe('DateDropdownComponent', () => {
let component: DateDropdownComponent
let httpTestingController: HttpTestingController
let settingsService: SettingsService
let settingsSpy
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
DateDropdownComponent,
ClearableBadgeComponent,
CustomDatePipe,
],
providers: [SettingsService, CustomDatePipe, DatePipe],
imports: [
HttpClientTestingModule,
NgbModule,
FormsModule,
ReactiveFormsModule,
],
}).compileComponents()
httpTestingController = TestBed.inject(HttpTestingController)
settingsService = TestBed.inject(SettingsService)
settingsSpy = jest.spyOn(settingsService, 'getLocalizedDateInputFormat')
fixture = TestBed.createComponent(DateDropdownComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should use a localized date placeholder', () => {
expect(component.datePlaceHolder).toEqual('mm/dd/yyyy')
expect(settingsSpy).toHaveBeenCalled()
})
it('should support date input, emit change', fakeAsync(() => {
let result: string
component.dateAfterChange.subscribe((date) => (result = date))
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
input.value = '5/30/2023'
input.dispatchEvent(new Event('change'))
tick(500)
expect(result).not.toBeNull()
}))
it('should support date select, emit datesSet change', fakeAsync(() => {
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
input.value = '5/30/2023'
input.dispatchEvent(new Event('dateSelect'))
tick(500)
expect(result).not.toBeNull()
}))
it('should support relative dates', fakeAsync(() => {
let result: DateSelection
component.datesSet.subscribe((date) => (result = date))
component.setRelativeDate(null)
component.setRelativeDate(RelativeDate.LAST_7_DAYS)
tick(500)
expect(result).toEqual({
after: null,
before: null,
relativeDateID: RelativeDate.LAST_7_DAYS,
})
}))
it('should support report if active', () => {
component.relativeDate = RelativeDate.LAST_7_DAYS
expect(component.isActive).toBeTruthy()
component.relativeDate = null
component.dateAfter = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.dateAfter = null
component.dateBefore = '2023-05-30'
expect(component.isActive).toBeTruthy()
component.dateBefore = null
expect(component.isActive).toBeFalsy()
})
it('should support reset', () => {
component.dateAfter = '2023-05-30'
component.reset()
expect(component.dateAfter).toBeNull()
})
it('should support clearAfter', () => {
component.dateAfter = '2023-05-30'
component.clearAfter()
expect(component.dateAfter).toBeNull()
})
it('should support clearBefore', () => {
component.dateBefore = '2023-05-30'
component.clearBefore()
expect(component.dateBefore).toBeNull()
})
it('should limit keyboard events', () => {
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
let event: KeyboardEvent = new KeyboardEvent('keypress', {
key: '9',
})
let eventSpy = jest.spyOn(event, 'preventDefault')
input.dispatchEvent(event)
expect(eventSpy).not.toHaveBeenCalled()
event = new KeyboardEvent('keypress', {
key: '{',
})
eventSpy = jest.spyOn(event, 'preventDefault')
input.dispatchEvent(event)
expect(eventSpy).toHaveBeenCalled()
})
})

View File

@@ -1,4 +1,3 @@
import { formatDate } from '@angular/common'
import {
Component,
EventEmitter,

View File

@@ -0,0 +1,55 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
describe('CorrespondentEditDialogComponent', () => {
let component: CorrespondentEditDialogComponent
let fixture: ComponentFixture<CorrespondentEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
CorrespondentEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,55 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
describe('DocumentTypeEditDialogComponent', () => {
let component: DocumentTypeEditDialogComponent
let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
DocumentTypeEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,234 @@
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { Component } from '@angular/core'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import {
FormGroup,
FormControl,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagService } from 'src/app/services/rest/tag.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_ALL,
MATCH_AUTO,
MATCH_NONE,
} from 'src/app/data/matching-model'
import { of } from 'rxjs'
import { environment } from 'src/environments/environment'
@Component({
template: `
<div>
<h4 class="modal-title" id="modal-basic-title">{{ getTitle() }}</h4>
</div>
`,
})
class TestComponent extends EditDialogComponent<PaperlessTag> {
constructor(
service: TagService,
activeModal: NgbActiveModal,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
}
getForm(): FormGroup<any> {
return new FormGroup({
name: new FormControl(''),
color: new FormControl(''),
is_inbox_tag: new FormControl(false),
permissions_form: new FormControl(null),
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
})
}
}
const currentUser = {
id: 99,
username: 'user99',
}
const permissions = {
view: {
users: [11],
groups: [],
},
change: {
users: [],
groups: [2],
},
}
const tag = {
id: 1,
name: 'Tag 1',
color: '#fff000',
is_inbox_tag: false,
matching_algorithm: MATCH_AUTO,
owner: 10,
permissions,
}
describe('EditDialogComponent', () => {
let component: TestComponent
let fixture: ComponentFixture<TestComponent>
let tagService: TagService
let activeModal: NgbActiveModal
let httpTestingController: HttpTestingController
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
NgbActiveModal,
{
provide: UserService,
useValue: {
listAll: () =>
of({
results: [
{
id: 13,
username: 'user1',
},
],
}),
},
},
{
provide: SettingsService,
useValue: {
currentUser,
},
},
TagService,
],
imports: [HttpClientTestingModule, FormsModule, ReactiveFormsModule],
}).compileComponents()
tagService = TestBed.inject(TagService)
activeModal = TestBed.inject(NgbActiveModal)
httpTestingController = TestBed.inject(HttpTestingController)
fixture = TestBed.createComponent(TestComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should interpolate object permissions', () => {
component.object = tag
component.dialogMode = EditDialogMode.EDIT
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value).toEqual({
owner: tag.owner,
set_permissions: permissions,
})
})
it('should delay close enabled', fakeAsync(() => {
expect(component.closeEnabled).toBeFalsy()
component.ngOnInit()
tick(100)
expect(component.closeEnabled).toBeTruthy()
}))
it('should set default owner when in create mode', () => {
component.dialogMode = EditDialogMode.CREATE
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value.owner).toEqual(
currentUser.id
)
// cover optional chaining
component.objectForm.removeControl('permissions_form')
component.ngOnInit()
})
it('should detect if pattern required', () => {
expect(component.patternRequired).toBeFalsy()
component.objectForm.get('matching_algorithm').setValue(MATCH_AUTO)
expect(component.patternRequired).toBeFalsy()
component.objectForm.get('matching_algorithm').setValue(MATCH_NONE)
expect(component.patternRequired).toBeFalsy()
component.objectForm.get('matching_algorithm').setValue(MATCH_ALL)
expect(component.patternRequired).toBeTruthy()
// coverage
component.objectForm = null
expect(component.patternRequired).toBeTruthy()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
// coverage
component.dialogMode = null
fixture.detectChanges()
})
it('should close on cancel', () => {
const closeSpy = jest.spyOn(activeModal, 'close')
component.cancel()
expect(closeSpy).toHaveBeenCalled()
})
it('should update an object on save in edit mode', () => {
const updateSpy = jest.spyOn(tagService, 'update')
component.dialogMode = EditDialogMode.EDIT
component.save()
expect(updateSpy).toHaveBeenCalled()
})
it('should create an object on save in edit mode', () => {
const createSpy = jest.spyOn(tagService, 'create')
component.dialogMode = EditDialogMode.CREATE
component.save()
expect(createSpy).toHaveBeenCalled()
})
it('should close on successful save', () => {
const closeSpy = jest.spyOn(activeModal, 'close')
const successSpy = jest.spyOn(component.succeeded, 'emit')
component.save()
httpTestingController.expectOne(`${environment.apiBaseUrl}tags/`).flush({})
expect(closeSpy).toHaveBeenCalled()
expect(successSpy).toHaveBeenCalled()
})
it('should not close on failed save', () => {
const closeSpy = jest.spyOn(activeModal, 'close')
const failedSpy = jest.spyOn(component.failed, 'next')
component.save()
httpTestingController
.expectOne(`${environment.apiBaseUrl}tags/`)
.flush('error', {
status: 500,
statusText: 'error',
})
expect(closeSpy).not.toHaveBeenCalled()
expect(failedSpy).toHaveBeenCalled()
expect(component.error).toEqual('error')
})
})

View File

@@ -15,6 +15,11 @@ import { UserService } from 'src/app/services/rest/user.service'
import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component'
import { SettingsService } from 'src/app/services/settings.service'
export enum EditDialogMode {
CREATE = 0,
EDIT = 1,
}
@Directive()
export abstract class EditDialogComponent<
T extends ObjectWithPermissions | ObjectWithId
@@ -30,7 +35,7 @@ export abstract class EditDialogComponent<
users: PaperlessUser[]
@Input()
dialogMode: string = 'create'
dialogMode: EditDialogMode = EditDialogMode.CREATE
@Input()
object: T
@@ -71,7 +76,7 @@ export abstract class EditDialogComponent<
this.userService.listAll().subscribe((r) => {
this.users = r.results
if (this.dialogMode === 'create') {
if (this.dialogMode === EditDialogMode.CREATE) {
this.objectForm.get('permissions_form')?.setValue({
owner: this.settingsService.currentUser.id,
})
@@ -87,15 +92,11 @@ export abstract class EditDialogComponent<
return $localize`Edit item`
}
getSaveErrorMessage(error: string) {
return $localize`Could not save element: ${error}`
}
getTitle() {
switch (this.dialogMode) {
case 'create':
case EditDialogMode.CREATE:
return this.getCreateTitle()
case 'edit':
case EditDialogMode.EDIT:
return this.getEditTitle()
default:
break
@@ -127,10 +128,10 @@ export abstract class EditDialogComponent<
var newObject = Object.assign(Object.assign({}, this.object), formValues)
var serverResponse: Observable<T>
switch (this.dialogMode) {
case 'create':
case EditDialogMode.CREATE:
serverResponse = this.service.create(newObject)
break
case 'edit':
case EditDialogMode.EDIT:
serverResponse = this.service.update(newObject)
default:
break

View File

@@ -0,0 +1,57 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { GroupEditDialogComponent } from './group-edit-dialog.component'
import { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
describe('GroupEditDialogComponent', () => {
let component: GroupEditDialogComponent
let fixture: ComponentFixture<GroupEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
GroupEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
PermissionsSelectComponent,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(GroupEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,117 @@
import {
ComponentFixture,
TestBed,
discardPeriodicTasks,
fakeAsync,
tick,
} from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { MailAccountEditDialogComponent } from './mail-account-edit-dialog.component'
import { PasswordComponent } from '../../input/password/password.component'
import { CheckComponent } from '../../input/check/check.component'
import { IMAPSecurity } from 'src/app/data/paperless-mail-account'
import { environment } from 'src/environments/environment'
describe('MailAccountEditDialogComponent', () => {
let component: MailAccountEditDialogComponent
let fixture: ComponentFixture<MailAccountEditDialogComponent>
let httpController: HttpTestingController
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
MailAccountEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
CheckComponent,
PermissionsFormComponent,
PasswordComponent,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
httpController = TestBed.inject(HttpTestingController)
fixture = TestBed.createComponent(MailAccountEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
it('should support test mail account and show appropriate expiring alert', fakeAsync(() => {
component.object = {
name: 'example',
imap_server: 'imap.example.com',
username: 'user',
password: 'pass',
imap_port: 443,
imap_security: IMAPSecurity.SSL,
is_token: false,
}
// success
component.test()
httpController
.expectOne(`${environment.apiBaseUrl}mail_accounts/test/`)
.flush({ success: true })
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain(
'Successfully connected'
)
tick(6000)
fixture.detectChanges()
expect(fixture.nativeElement.textContent).not.toContain(
'Successfully connected'
)
// not success
component.test()
httpController
.expectOne(`${environment.apiBaseUrl}mail_accounts/test/`)
.flush({ success: false })
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('Unable to connect')
// error
component.test()
httpController
.expectOne(`${environment.apiBaseUrl}mail_accounts/test/`)
.flush({}, { status: 500, statusText: 'error' })
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('Unable to connect')
tick(6000)
}))
})

View File

@@ -0,0 +1,113 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { MailRuleEditDialogComponent } from './mail-rule-edit-dialog.component'
import { NumberComponent } from '../../input/number/number.component'
import { TagsComponent } from '../../input/tags/tags.component'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { of } from 'rxjs'
import {
MailAction,
MailMetadataCorrespondentOption,
} from 'src/app/data/paperless-mail-rule'
describe('MailRuleEditDialogComponent', () => {
let component: MailRuleEditDialogComponent
let fixture: ComponentFixture<MailRuleEditDialogComponent>
let accountService: MailAccountService
let correspondentService: CorrespondentService
let documentTypeService: DocumentTypeService
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
MailRuleEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
NumberComponent,
TagsComponent,
SafeHtmlPipe,
],
providers: [
NgbActiveModal,
{
provide: MailAccountService,
useValue: {
listAll: () => of([]),
},
},
{
provide: CorrespondentService,
useValue: {
listAll: () => of([]),
},
},
{
provide: DocumentTypeService,
useValue: {
listAll: () => of([]),
},
},
],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(MailRuleEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
it('should support optional fields', () => {
expect(component.showCorrespondentField).toBeFalsy()
component.objectForm
.get('assign_correspondent_from')
.setValue(MailMetadataCorrespondentOption.FromCustom)
expect(component.showCorrespondentField).toBeTruthy()
expect(component.showActionParamField).toBeFalsy()
component.objectForm.get('action').setValue(MailAction.Move)
expect(component.showActionParamField).toBeTruthy()
component.objectForm.get('action').setValue('')
expect(component.showActionParamField).toBeFalsy()
component.objectForm.get('action').setValue(MailAction.Tag)
expect(component.showActionParamField).toBeTruthy()
// coverage of optional chaining
component.objectForm = null
expect(component.showCorrespondentField).toBeFalsy()
expect(component.showActionParamField).toBeFalsy()
})
})

View File

@@ -0,0 +1,57 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { StoragePathEditDialogComponent } from './storage-path-edit-dialog.component'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
describe('StoragePathEditDialogComponent', () => {
let component: StoragePathEditDialogComponent
let fixture: ComponentFixture<StoragePathEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
StoragePathEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
SafeHtmlPipe,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(StoragePathEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,59 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { TagEditDialogComponent } from './tag-edit-dialog.component'
import { ColorComponent } from '../../input/color/color.component'
import { CheckComponent } from '../../input/check/check.component'
describe('TagEditDialogComponent', () => {
let component: TagEditDialogComponent
let fixture: ComponentFixture<TagEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
TagEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PermissionsFormComponent,
ColorComponent,
CheckComponent,
],
providers: [NgbActiveModal],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(TagEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,115 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import {
AbstractControl,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { UserEditDialogComponent } from './user-edit-dialog.component'
import { PasswordComponent } from '../../input/password/password.component'
import { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
import { GroupService } from 'src/app/services/rest/group.service'
import { of } from 'rxjs'
describe('UserEditDialogComponent', () => {
let component: UserEditDialogComponent
let fixture: ComponentFixture<UserEditDialogComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
UserEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
PasswordComponent,
PermissionsFormComponent,
PermissionsSelectComponent,
],
providers: [
NgbActiveModal,
{
provide: GroupService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
permissions: ['dummy_perms'],
},
],
}),
},
},
],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(UserEditDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
it('should disable user permissions select on toggle superuser', () => {
const control: AbstractControl =
component.objectForm.get('user_permissions')
expect(control.disabled).toBeFalsy()
component.objectForm.get('is_superuser').setValue(true)
component.onToggleSuperUser()
expect(control.disabled).toBeTruthy()
})
it('should update inherited permissions', () => {
component.objectForm.get('groups').setValue(null)
expect(component.inheritedPermissions).toEqual([])
component.objectForm.get('groups').setValue([1])
expect(component.inheritedPermissions).toEqual(['dummy_perms'])
component.objectForm.get('groups').setValue([2])
expect(component.inheritedPermissions).toEqual([])
})
it('should detect whether password was changed in form on save', () => {
component.objectForm.get('password').setValue(null)
component.save()
expect(component.passwordIsSet).toBeFalsy()
// unchanged pw
component.objectForm.get('password').setValue('*******')
component.save()
expect(component.passwordIsSet).toBeFalsy()
// unchanged pw
component.objectForm.get('password').setValue('helloworld')
component.save()
expect(component.passwordIsSet).toBeTruthy()
})
})

View File

@@ -34,7 +34,7 @@
<div *ngIf="selectionModel.items" class="items" #buttonItems>
<ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText; let i = index">
<app-toggleable-dropdown-button
*ngIf="allowSelectNone || item.id" [item]="item" [hideCount]="hideCount(item)" [state]="selectionModel.get(item.id)" [count]="getUpdatedDocumentCount(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" (click)="setButtonItemIndex(i)" [disabled]="disabled">
*ngIf="allowSelectNone || item.id" [item]="item" [hideCount]="hideCount(item)" [state]="selectionModel.get(item.id)" [count]="getUpdatedDocumentCount(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" (click)="setButtonItemIndex(i - 1)" [disabled]="disabled">
</app-toggleable-dropdown-button>
</ng-container>
</div>

View File

@@ -0,0 +1,487 @@
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import {
ChangedItems,
FilterableDropdownComponent,
FilterableDropdownSelectionModel,
Intersection,
LogicalOperator,
} from './filterable-dropdown.component'
import { FilterPipe } from 'src/app/pipes/filter.pipe'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_ALL,
} from 'src/app/data/matching-model'
import {
ToggleableDropdownButtonComponent,
ToggleableItemState,
} from './toggleable-dropdown-button/toggleable-dropdown-button.component'
import { TagComponent } from '../tag/tag.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
const items: PaperlessTag[] = [
{
id: 1,
name: 'Tag1',
is_inbox_tag: false,
matching_algorithm: DEFAULT_MATCHING_ALGORITHM,
},
{
id: 2,
name: 'Tag2',
is_inbox_tag: true,
matching_algorithm: MATCH_ALL,
match: 'str',
},
]
const nullItem = {
id: null,
name: 'Not assigned',
}
let selectionModel: FilterableDropdownSelectionModel
describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => {
let component: FilterableDropdownComponent
let fixture: ComponentFixture<FilterableDropdownComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
FilterableDropdownComponent,
FilterPipe,
ToggleableDropdownButtonComponent,
TagComponent,
ClearableBadgeComponent,
],
providers: [FilterPipe],
imports: [NgbModule, FormsModule, ReactiveFormsModule],
}).compileComponents()
fixture = TestBed.createComponent(FilterableDropdownComponent)
component = fixture.componentInstance
selectionModel = new FilterableDropdownSelectionModel()
})
it('should sanitize title', () => {
expect(component.name).toBeNull()
component.title = 'Foo Bar'
expect(component.name).toEqual('foo_bar')
})
it('should support reset', () => {
component.items = items
component.selectionModel = selectionModel
selectionModel.set(items[0].id, ToggleableItemState.Selected)
expect(selectionModel.getSelectedItems()).toHaveLength(1)
expect(selectionModel.isDirty()).toBeTruthy()
component.reset()
expect(selectionModel.getSelectedItems()).toHaveLength(0)
expect(selectionModel.isDirty()).toBeFalsy()
})
it('should report document counts', () => {
component.documentCounts = [
{
id: items[0].id,
document_count: 12,
},
]
expect(component.getUpdatedDocumentCount(items[0].id)).toEqual(12)
expect(component.getUpdatedDocumentCount(items[1].id)).toBeUndefined() // coverate of optional chaining
})
it('should emit change when items selected', () => {
component.items = items
component.selectionModel = selectionModel
let newModel: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe((model) => (newModel = model))
expect(newModel).toBeUndefined()
selectionModel.set(items[0].id, ToggleableItemState.Selected)
expect(selectionModel.isDirty()).toBeTruthy()
expect(newModel.getSelectedItems()).toEqual([items[0]])
expect(newModel.getExcludedItems()).toEqual([])
selectionModel.set(items[0].id, ToggleableItemState.NotSelected)
expect(newModel.getSelectedItems()).toEqual([])
expect(component.items).toEqual([nullItem, ...items])
})
it('should emit change when items excluded', () => {
component.items = items
component.selectionModel = selectionModel
let newModel: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe((model) => (newModel = model))
expect(newModel).toBeUndefined()
selectionModel.toggle(items[0].id)
expect(newModel.getSelectedItems()).toEqual([items[0]])
})
it('should emit change when items excluded', () => {
component.items = items
component.selectionModel = selectionModel
let newModel: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe((model) => (newModel = model))
selectionModel.set(items[0].id, ToggleableItemState.Excluded)
expect(newModel.getSelectedItems()).toEqual([])
expect(newModel.getExcludedItems()).toEqual([items[0]])
selectionModel.set(items[0].id, ToggleableItemState.NotSelected)
expect(newModel.getSelectedItems()).toEqual([])
expect(newModel.getExcludedItems()).toEqual([])
})
it('should exclude items when excluded and not editing', () => {
component.items = items
component.manyToOne = true
component.selectionModel = selectionModel
selectionModel.set(items[0].id, ToggleableItemState.Selected)
component.excludeClicked(items[0].id)
expect(selectionModel.getSelectedItems()).toEqual([])
expect(selectionModel.getExcludedItems()).toEqual([items[0]])
})
it('should toggle when items excluded and editing', () => {
component.items = items
component.manyToOne = true
component.editing = true
component.selectionModel = selectionModel
selectionModel.set(items[0].id, ToggleableItemState.NotSelected)
component.excludeClicked(items[0].id)
expect(selectionModel.getSelectedItems()).toEqual([items[0]])
expect(selectionModel.getExcludedItems()).toEqual([])
})
it('should hide count for item if adding will increase size of set', () => {
component.items = items
component.manyToOne = true
component.selectionModel = selectionModel
expect(component.hideCount(items[0])).toBeFalsy()
selectionModel.logicalOperator = LogicalOperator.Or
expect(component.hideCount(items[0])).toBeTruthy()
})
it('should enforce single select when editing', () => {
component.editing = true
component.items = items
component.selectionModel = selectionModel
let newModel: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe((model) => (newModel = model))
expect(selectionModel.singleSelect).toEqual(true)
selectionModel.toggle(items[0].id)
selectionModel.toggle(items[1].id)
expect(newModel.getSelectedItems()).toEqual([items[1]])
})
it('should support manyToOne selecting', () => {
component.items = items
selectionModel.manyToOne = false
component.selectionModel = selectionModel
component.manyToOne = true
expect(component.manyToOne).toBeTruthy()
let newModel: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe((model) => (newModel = model))
expect(selectionModel.singleSelect).toEqual(false)
selectionModel.toggle(items[0].id)
selectionModel.toggle(items[1].id)
expect(newModel.getSelectedItems()).toEqual([items[0], items[1]])
})
it('should dynamically enable / disable modifier toggle', () => {
component.items = items
component.selectionModel = selectionModel
expect(component.modifierToggleEnabled).toBeTruthy()
selectionModel.toggle(null)
expect(component.modifierToggleEnabled).toBeFalsy()
component.manyToOne = true
expect(component.modifierToggleEnabled).toBeFalsy()
selectionModel.toggle(items[0].id)
selectionModel.toggle(items[1].id)
expect(component.modifierToggleEnabled).toBeTruthy()
})
it('should apply changes and close when apply button clicked', () => {
component.items = items
component.editing = true
component.selectionModel = selectionModel
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
selectionModel.toggle(items[0].id)
fixture.detectChanges()
expect(component.modelIsDirty).toBeTruthy()
let applyResult: ChangedItems
const closeSpy = jest.spyOn(component.dropdown, 'close')
component.apply.subscribe((result) => (applyResult = result))
const applyButton = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).find((b) => b.textContent.includes('Apply'))
applyButton.dispatchEvent(new MouseEvent('click'))
expect(closeSpy).toHaveBeenCalled()
expect(applyResult).toEqual({ itemsToAdd: [items[0]], itemsToRemove: [] })
})
it('should apply on close if enabled', () => {
component.items = items
component.editing = true
component.applyOnClose = true
component.selectionModel = selectionModel
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
selectionModel.toggle(items[0].id)
fixture.detectChanges()
expect(component.modelIsDirty).toBeTruthy()
let applyResult: ChangedItems
component.apply.subscribe((result) => (applyResult = result))
component.dropdown.close()
expect(applyResult).toEqual({ itemsToAdd: [items[0]], itemsToRemove: [] })
})
it('should focus text filter on open, support filtering, clear on close', fakeAsync(() => {
component.items = items
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
expect(document.activeElement).toEqual(
component.listFilterTextInput.nativeElement
)
expect(
Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).filter((b) => b.textContent.includes('Tag'))
).toHaveLength(2)
component.filterText = 'Tag2'
fixture.detectChanges()
expect(
Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).filter((b) => b.textContent.includes('Tag'))
).toHaveLength(1)
component.dropdown.close()
expect(component.filterText).toHaveLength(0)
}))
it('should toggle & close on enter inside filter field if 1 item remains', fakeAsync(() => {
component.items = items
expect(component.selectionModel.getSelectedItems()).toEqual([])
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
component.filterText = 'Tag2'
fixture.detectChanges()
const closeSpy = jest.spyOn(component.dropdown, 'close')
component.listFilterTextInput.nativeElement.dispatchEvent(
new KeyboardEvent('keyup', { key: 'Enter' })
)
expect(component.selectionModel.getSelectedItems()).toEqual([items[1]])
tick(300)
expect(closeSpy).toHaveBeenCalled()
}))
it('should apply & close on enter inside filter field if 1 item remains if editing', fakeAsync(() => {
component.items = items
component.editing = true
let applyResult: ChangedItems
component.apply.subscribe((result) => (applyResult = result))
expect(component.selectionModel.getSelectedItems()).toEqual([])
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
component.filterText = 'Tag2'
fixture.detectChanges()
component.listFilterTextInput.nativeElement.dispatchEvent(
new KeyboardEvent('keyup', { key: 'Enter' })
)
expect(component.selectionModel.getSelectedItems()).toEqual([items[1]])
tick(300)
expect(applyResult).toEqual({ itemsToAdd: [items[1]], itemsToRemove: [] })
}))
it('should support arrow keyboard navigation', fakeAsync(() => {
component.items = items
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
const filterInputEl: HTMLInputElement =
component.listFilterTextInput.nativeElement
expect(document.activeElement).toEqual(filterInputEl)
const itemButtons = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).filter((b) => b.textContent.includes('Tag'))
filterInputEl.dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[0])
itemButtons[0].dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[1])
itemButtons[1].dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[0])
itemButtons[0].dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true })
)
expect(document.activeElement).toEqual(filterInputEl)
filterInputEl.value = 'foo'
component.filterText = 'foo'
// dont move focus if we're traversing the field
filterInputEl.selectionStart = 1
expect(document.activeElement).toEqual(filterInputEl)
// now we're at end, so move focus
filterInputEl.selectionStart = 3
filterInputEl.dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[0])
}))
it('should support arrow keyboard navigation after tab keyboard navigation', fakeAsync(() => {
component.items = items
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
const filterInputEl: HTMLInputElement =
component.listFilterTextInput.nativeElement
expect(document.activeElement).toEqual(filterInputEl)
const itemButtons = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).filter((b) => b.textContent.includes('Tag'))
filterInputEl.dispatchEvent(
new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
)
itemButtons[0].focus() // normally handled by browser
itemButtons[0].dispatchEvent(
new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })
)
itemButtons[1].focus() // normally handled by browser
itemButtons[1].dispatchEvent(
new KeyboardEvent('keydown', {
key: 'Tab',
shiftKey: true,
bubbles: true,
})
)
itemButtons[0].focus() // normally handled by browser
itemButtons[0].dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[1])
}))
it('should support arrow keyboard navigation after click', fakeAsync(() => {
component.items = items
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
const filterInputEl: HTMLInputElement =
component.listFilterTextInput.nativeElement
expect(document.activeElement).toEqual(filterInputEl)
const itemButtons = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('button')
).filter((b) => b.textContent.includes('Tag'))
fixture.nativeElement
.querySelector('app-toggleable-dropdown-button')
.dispatchEvent(new MouseEvent('click'))
itemButtons[0].focus() // normally handled by browser
expect(document.activeElement).toEqual(itemButtons[0])
itemButtons[0].dispatchEvent(
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
)
expect(document.activeElement).toEqual(itemButtons[1])
}))
it('should toggle logical operator', fakeAsync(() => {
component.items = items
component.manyToOne = true
selectionModel.set(items[0].id, ToggleableItemState.Selected)
selectionModel.set(items[1].id, ToggleableItemState.Selected)
component.selectionModel = selectionModel
let changedResult: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe(
(result) => (changedResult = result)
)
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
expect(component.modifierToggleEnabled).toBeTruthy()
const operatorButtons: HTMLInputElement[] = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('input')
).filter((b) => ['and', 'or'].includes(b.value))
expect(operatorButtons[0].checked).toBeTruthy()
operatorButtons[1].dispatchEvent(new MouseEvent('click'))
fixture.detectChanges()
expect(selectionModel.logicalOperator).toEqual(LogicalOperator.Or)
expect(changedResult.logicalOperator).toEqual(LogicalOperator.Or)
}))
it('should toggle intersection include / exclude', fakeAsync(() => {
component.items = items
selectionModel.set(items[0].id, ToggleableItemState.Selected)
selectionModel.set(items[1].id, ToggleableItemState.Selected)
component.selectionModel = selectionModel
let changedResult: FilterableDropdownSelectionModel
component.selectionModelChange.subscribe(
(result) => (changedResult = result)
)
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
expect(component.modifierToggleEnabled).toBeTruthy()
const intersectionButtons: HTMLInputElement[] = Array.from(
(fixture.nativeElement as HTMLDivElement).querySelectorAll('input')
).filter((b) => ['include', 'exclude'].includes(b.value))
expect(intersectionButtons[0].checked).toBeTruthy()
intersectionButtons[1].dispatchEvent(new MouseEvent('click'))
fixture.detectChanges()
expect(selectionModel.intersection).toEqual(Intersection.Exclude)
expect(changedResult.intersection).toEqual(Intersection.Exclude)
expect(changedResult.getSelectedItems()).toEqual([])
expect(changedResult.getExcludedItems()).toEqual(items)
}))
it('FilterableDropdownSelectionModel should sort items by state', () => {
component.items = items
component.selectionModel = selectionModel
selectionModel.toggle(items[1].id)
selectionModel.apply()
expect(selectionModel.itemsSorted).toEqual([nullItem, items[1], items[0]])
})
})

View File

@@ -96,7 +96,7 @@ export class FilterableDropdownSelectionModel {
toggle(id: number, fireEvent = true) {
let state = this.temporarySelectionStates.get(id)
if (
state == null ||
state == undefined ||
(state != ToggleableItemState.Selected &&
state != ToggleableItemState.Excluded)
) {

View File

@@ -0,0 +1,79 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
ToggleableDropdownButtonComponent,
ToggleableItemState,
} from './toggleable-dropdown-button.component'
import { TagComponent } from '../../tag/tag.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'
describe('ToggleableDropdownButtonComponent', () => {
let component: ToggleableDropdownButtonComponent
let fixture: ComponentFixture<ToggleableDropdownButtonComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ToggleableDropdownButtonComponent, TagComponent],
providers: [],
imports: [],
}).compileComponents()
fixture = TestBed.createComponent(ToggleableDropdownButtonComponent)
component = fixture.componentInstance
})
it('should recognize a tag', () => {
component.item = {
id: 1,
name: 'Test Tag',
is_inbox_tag: false,
} as PaperlessTag
fixture.detectChanges()
expect(component.isTag).toBeTruthy()
})
it('should report toggled state', () => {
expect(component.isChecked()).toBeFalsy()
expect(component.isPartiallyChecked()).toBeFalsy()
expect(component.isExcluded()).toBeFalsy()
component.state = ToggleableItemState.Selected
expect(component.isChecked()).toBeTruthy()
expect(component.isPartiallyChecked()).toBeFalsy()
expect(component.isExcluded()).toBeFalsy()
component.state = ToggleableItemState.PartiallySelected
expect(component.isPartiallyChecked()).toBeTruthy()
expect(component.isChecked()).toBeFalsy()
expect(component.isExcluded()).toBeFalsy()
component.state = ToggleableItemState.Excluded
expect(component.isExcluded()).toBeTruthy()
expect(component.isChecked()).toBeFalsy()
expect(component.isPartiallyChecked()).toBeFalsy()
})
it('should emit exclude event when selected and then toggled', () => {
let excludeResult
let toggleResult
component.state = ToggleableItemState.Selected
component.exclude.subscribe(() => (excludeResult = true))
component.toggle.subscribe(() => (toggleResult = true))
const button = fixture.nativeElement.querySelector('button')
button.dispatchEvent(new MouseEvent('click'))
expect(excludeResult).toBeTruthy()
expect(toggleResult).toBeFalsy()
})
it('should emit toggle event when not selected and then toggled', () => {
let excludeResult
let toggleResult
component.state = ToggleableItemState.Excluded
component.exclude.subscribe(() => (excludeResult = true))
component.toggle.subscribe(() => (toggleResult = true))
const button = fixture.nativeElement.querySelector('button')
button.dispatchEvent(new MouseEvent('click'))
expect(excludeResult).toBeFalsy()
expect(toggleResult).toBeTruthy()
})
})

View File

@@ -0,0 +1,55 @@
import { Component } from '@angular/core'
import { AbstractInputComponent } from './abstract-input'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
@Component({
template: `
<div>
<input
#inputField
type="text"
class="form-control"
[class.is-invalid]="error"
[id]="inputId"
[(ngModel)]="value"
(change)="onChange(value)"
[disabled]="disabled"
/>
</div>
`,
})
class TestComponent extends AbstractInputComponent<string> {
constructor() {
super()
}
}
describe(`AbstractInputComponent`, () => {
let component: TestComponent
let fixture: ComponentFixture<TestComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents()
fixture = TestBed.createComponent(TestComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should assign uuid', () => {
component.ngOnInit()
expect(component.inputId).not.toBeUndefined()
})
it('should support focus', () => {
const focusSpy = jest.spyOn(component.inputField.nativeElement, 'focus')
component.focus()
expect(focusSpy).toHaveBeenCalled()
})
})

View File

@@ -1,5 +1,5 @@
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
<input #inputField type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
<label class="form-check-label" [for]="inputId">{{title}}</label>
<div *ngIf="hint" class="form-text text-muted">{{hint}}</div>
</div>

View File

@@ -0,0 +1,39 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CheckComponent } from './check.component'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
describe('CheckComponent', () => {
let component: CheckComponent
let fixture: ComponentFixture<CheckComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [CheckComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents()
fixture = TestBed.createComponent(CheckComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should support use of checkbox', () => {
input.checked = true
input.dispatchEvent(new Event('change'))
fixture.detectChanges()
expect(component.value).toBeTruthy()
input.checked = false
input.dispatchEvent(new Event('change'))
fixture.detectChanges()
expect(component.value).toBeFalsy()
})
})

View File

@@ -1,6 +1,5 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { v4 as uuidv4 } from 'uuid'
import { Component, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
@Component({

View File

@@ -11,7 +11,7 @@
</ng-template>
<input class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [autoClose]="'outside'" [ngbPopover]="popContent" placement="bottom" popoverClass="shadow">
<input #inputField class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [autoClose]="'outside'" [ngbPopover]="popContent" placement="bottom" popoverClass="shadow">
<button class="btn btn-outline-secondary" type="button" (click)="randomize()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dice-5" viewBox="0 0 16 16">

View File

@@ -0,0 +1,72 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { ColorComponent } from './color.component'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { ColorSliderModule } from 'ngx-color/slider'
describe('ColorComponent', () => {
let component: ColorComponent
let fixture: ComponentFixture<ColorComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ColorComponent],
providers: [],
imports: [
FormsModule,
ReactiveFormsModule,
NgbPopoverModule,
ColorSliderModule,
],
}).compileComponents()
fixture = TestBed.createComponent(ColorComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should support use of input', () => {
input.value = '#ff0000'
component.colorChanged(input.value)
fixture.detectChanges()
expect(component.value).toEqual('#ff0000')
})
it('should set swatch color', () => {
const swatch: HTMLSpanElement = fixture.nativeElement.querySelector(
'span.input-group-text'
)
expect(swatch.style.backgroundColor).toEqual('')
component.value = '#ff0000'
fixture.detectChanges()
expect(swatch.style.backgroundColor).toEqual('rgb(255, 0, 0)')
})
it('should show color slider popover', () => {
component.value = '#ff0000'
input.dispatchEvent(new MouseEvent('click'))
fixture.detectChanges()
expect(
fixture.nativeElement.querySelector('ngb-popover-window')
).not.toBeUndefined()
expect(
fixture.nativeElement.querySelector('color-slider')
).not.toBeUndefined()
fixture.nativeElement
.querySelector('color-slider')
.dispatchEvent(new Event('change'))
})
it('should allow randomize color and update value', () => {
expect(component.value).toBeUndefined()
component.randomize()
expect(component.value).not.toBeUndefined()
})
})

View File

@@ -1,7 +1,7 @@
<div class="mb-3">
<label class="form-label" [for]="inputId">{{title}}</label>
<div class="input-group" [class.is-invalid]="error">
<input class="form-control" [class.is-invalid]="error" [placeholder]="placeholder" [id]="inputId" maxlength="10"
<input #inputField class="form-control" [class.is-invalid]="error" [placeholder]="placeholder" [id]="inputId" maxlength="10"
(dateSelect)="onChange(value)" (change)="onChange(value)" (keypress)="onKeyPress($event)" (paste)="onPaste($event)"
name="dp" [(ngModel)]="value" ngbDatepicker #datePicker="ngbDatepicker" #datePickerContent="ngModel" [disabled]="disabled">
<button class="btn btn-outline-secondary calendar" (click)="datePicker.toggle()" type="button" [disabled]="disabled">

View File

@@ -0,0 +1,103 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { DateComponent } from './date.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import {
NgbDateParserFormatter,
NgbDatepickerModule,
} from '@ng-bootstrap/ng-bootstrap'
import { RouterTestingModule } from '@angular/router/testing'
import { LocalizedDateParserFormatter } from 'src/app/utils/ngb-date-parser-formatter'
describe('DateComponent', () => {
let component: DateComponent
let fixture: ComponentFixture<DateComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [DateComponent],
providers: [
{
provide: NgbDateParserFormatter,
useClass: LocalizedDateParserFormatter,
},
],
imports: [
FormsModule,
ReactiveFormsModule,
HttpClientTestingModule,
NgbDatepickerModule,
RouterTestingModule,
],
}).compileComponents()
fixture = TestBed.createComponent(DateComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should support use of input field', () => {
input.value = '5/14/20'
input.dispatchEvent(new Event('change'))
fixture.detectChanges()
expect(component.value).toEqual({ day: 14, month: 5, year: 2020 })
})
it('should use localzed placeholder from settings', () => {
component.ngOnInit()
expect(component.placeholder).toEqual('mm/dd/yyyy')
})
it('should support suggestions', () => {
expect(component.value).toBeUndefined()
component.suggestions = ['2023-05-31', '2014-05-14']
fixture.detectChanges()
const suggestionAnchor: HTMLAnchorElement =
fixture.nativeElement.querySelector('a')
suggestionAnchor.click()
expect(component.value).toEqual({ day: 31, month: 5, year: 2023 })
})
it('should limit keyboard events', () => {
let event: KeyboardEvent = new KeyboardEvent('keypress', {
key: '9',
})
let eventSpy = jest.spyOn(event, 'preventDefault')
input.dispatchEvent(event)
expect(eventSpy).not.toHaveBeenCalled()
event = new KeyboardEvent('keypress', {
key: '{',
})
eventSpy = jest.spyOn(event, 'preventDefault')
input.dispatchEvent(event)
expect(eventSpy).toHaveBeenCalled()
})
it('should support paste', () => {
expect(component.value).toBeUndefined()
const date = '5/4/20'
const clipboardData = {
dropEffect: null,
effectAllowed: null,
files: null,
items: null,
types: null,
clearData: null,
getData: () => date,
setData: null,
setDragImage: null,
}
const event = new Event('paste')
event['clipboardData'] = clipboardData
input.dispatchEvent(event)
expect(component.value).toEqual({ day: 4, month: 5, year: 2020 })
})
})

View File

@@ -1,7 +1,7 @@
<div class="mb-3">
<label class="form-label" [for]="inputId">{{title}}</label>
<div class="input-group" [class.is-invalid]="error">
<input type="number" class="form-control" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [class.is-invalid]="error" [disabled]="disabled">
<input #inputField type="number" class="form-control" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [class.is-invalid]="error" [disabled]="disabled">
<button *ngIf="showAdd" class="btn btn-outline-secondary" type="button" id="button-addon1" (click)="nextAsn()" [disabled]="disabled">+1</button>
</div>
<div class="invalid-feedback">

View File

@@ -0,0 +1,79 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { NumberComponent } from './number.component'
import { DocumentService } from 'src/app/services/rest/document.service'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { of } from 'rxjs'
describe('NumberComponent', () => {
let component: NumberComponent
let fixture: ComponentFixture<NumberComponent>
let input: HTMLInputElement
let documentService: DocumentService
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [NumberComponent],
providers: [DocumentService],
imports: [FormsModule, ReactiveFormsModule, HttpClientTestingModule],
}).compileComponents()
fixture = TestBed.createComponent(NumberComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
documentService = TestBed.inject(DocumentService)
fixture.detectChanges()
input = component.inputField.nativeElement
})
// TODO: why doesnt this work?
// it('should support use of input field', () => {
// expect(component.value).toBeUndefined()
// input.stepUp()
// console.log(input.value);
// input.dispatchEvent(new Event('change'))
// fixture.detectChanges()
// expect(component.value).toEqual('3')
// })
it('should support +1 ASN', () => {
const listAllSpy = jest.spyOn(documentService, 'listFiltered')
listAllSpy
.mockReturnValueOnce(
of({
count: 1,
all: [1],
results: [
{
id: 1,
archive_serial_number: 1000,
},
],
})
)
.mockReturnValueOnce(
of({
count: 0,
all: [],
results: [],
})
)
expect(component.value).toBeUndefined()
component.nextAsn()
expect(component.value).toEqual(1001)
// this time results are empty
component.value = undefined
component.nextAsn()
expect(component.value).toEqual(1)
component.value = 1002
component.nextAsn()
expect(component.value).toEqual(1002)
})
})

View File

@@ -0,0 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
ReactiveFormsModule,
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { PasswordComponent } from './password.component'
describe('PasswordComponent', () => {
let component: PasswordComponent
let fixture: ComponentFixture<PasswordComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [PasswordComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents()
fixture = TestBed.createComponent(PasswordComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should support use of input field', () => {
expect(component.value).toBeUndefined()
// TODO: why doesnt this work?
// input.value = 'foo'
// input.dispatchEvent(new Event('change'))
// fixture.detectChanges()
// expect(component.value).toEqual('foo')
})
})

View File

@@ -0,0 +1,66 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
ReactiveFormsModule,
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { PermissionsFormComponent } from './permissions-form.component'
import { SelectComponent } from '../../select/select.component'
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
import { PermissionsGroupComponent } from '../permissions-group/permissions-group.component'
import { PermissionsUserComponent } from '../permissions-user/permissions-user.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { NgSelectModule } from '@ng-select/ng-select'
describe('PermissionsFormComponent', () => {
let component: PermissionsFormComponent
let fixture: ComponentFixture<PermissionsFormComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
PermissionsFormComponent,
SelectComponent,
PermissionsGroupComponent,
PermissionsUserComponent,
],
providers: [],
imports: [
FormsModule,
ReactiveFormsModule,
NgbAccordionModule,
HttpClientTestingModule,
NgSelectModule,
],
}).compileComponents()
fixture = TestBed.createComponent(PermissionsFormComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support use of select for owner', () => {
const changeSpy = jest.spyOn(component, 'onChange')
component.ngOnInit()
component.users = [
{
id: 2,
username: 'foo',
},
{
id: 3,
username: 'bar',
},
]
component.form.get('owner').patchValue(2)
fixture.detectChanges()
expect(changeSpy).toHaveBeenCalledWith({
owner: 2,
set_permissions: {
view: { users: [], groups: [] },
change: { users: [], groups: [] },
},
})
})
})

View File

@@ -0,0 +1,59 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { PermissionsGroupComponent } from './permissions-group.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { NgSelectModule } from '@ng-select/ng-select'
import { GroupService } from 'src/app/services/rest/group.service'
import { of } from 'rxjs'
describe('PermissionsGroupComponent', () => {
let component: PermissionsGroupComponent
let fixture: ComponentFixture<PermissionsGroupComponent>
let groupService: GroupService
let groupServiceSpy
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [PermissionsGroupComponent],
providers: [GroupService],
imports: [
FormsModule,
ReactiveFormsModule,
HttpClientTestingModule,
NgSelectModule,
],
}).compileComponents()
groupService = TestBed.inject(GroupService)
groupServiceSpy = jest.spyOn(groupService, 'listAll').mockReturnValue(
of({
count: 2,
all: [2, 3],
results: [
{
id: 2,
name: 'Group 2',
},
{
id: 3,
name: 'Group 3',
},
],
})
)
fixture = TestBed.createComponent(PermissionsGroupComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should get groups, support use of select', () => {
component.writeValue({ id: 2, name: 'Group 2' })
expect(component.value).toEqual({ id: 2, name: 'Group 2' })
expect(groupServiceSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,60 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { PermissionsUserComponent } from './permissions-user.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { NgSelectModule } from '@ng-select/ng-select'
import { GroupService } from 'src/app/services/rest/group.service'
import { of } from 'rxjs'
import { UserService } from 'src/app/services/rest/user.service'
describe('PermissionsUserComponent', () => {
let component: PermissionsUserComponent
let fixture: ComponentFixture<PermissionsUserComponent>
let userService: UserService
let userServiceSpy
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [PermissionsUserComponent],
providers: [UserService],
imports: [
FormsModule,
ReactiveFormsModule,
HttpClientTestingModule,
NgSelectModule,
],
}).compileComponents()
userService = TestBed.inject(UserService)
userServiceSpy = jest.spyOn(userService, 'listAll').mockReturnValue(
of({
count: 2,
all: [2, 3],
results: [
{
id: 2,
name: 'User 2',
},
{
id: 3,
name: 'User 3',
},
],
})
)
fixture = TestBed.createComponent(PermissionsUserComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should get users, support use of select', () => {
component.writeValue({ id: 2, name: 'User 2' })
expect(component.value).toEqual({ id: 2, name: 'User 2' })
expect(userServiceSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,121 @@
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import {
FormsModule,
ReactiveFormsModule,
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { SelectComponent } from './select.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_ALL,
} from 'src/app/data/matching-model'
import { NgSelectModule } from '@ng-select/ng-select'
import { RouterTestingModule } from '@angular/router/testing'
const items: PaperlessTag[] = [
{
id: 1,
name: 'Tag1',
is_inbox_tag: false,
matching_algorithm: DEFAULT_MATCHING_ALGORITHM,
},
{
id: 2,
name: 'Tag2',
is_inbox_tag: true,
matching_algorithm: MATCH_ALL,
match: 'str',
},
{
id: 10,
name: 'Tag10',
is_inbox_tag: false,
matching_algorithm: DEFAULT_MATCHING_ALGORITHM,
},
]
describe('SelectComponent', () => {
let component: SelectComponent
let fixture: ComponentFixture<SelectComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [SelectComponent],
providers: [],
imports: [
FormsModule,
ReactiveFormsModule,
NgSelectModule,
RouterTestingModule,
],
}).compileComponents()
fixture = TestBed.createComponent(SelectComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support private items', () => {
component.value = 3
component.items = items
expect(component.items).toContainEqual({
id: 3,
name: 'Private',
private: true,
})
component.checkForPrivateItems([4, 5])
expect(component.items).toContainEqual({
id: 4,
name: 'Private',
private: true,
})
expect(component.items).toContainEqual({
id: 5,
name: 'Private',
private: true,
})
})
it('should support suggestions', () => {
expect(component.value).toBeUndefined()
component.items = items
component.suggestions = [1, 2]
fixture.detectChanges()
const suggestionAnchor: HTMLAnchorElement =
fixture.nativeElement.querySelector('a')
suggestionAnchor.click()
expect(component.value).toEqual(1)
})
it('should support create new and emit the value', () => {
expect(component.allowCreateNew).toBeFalsy()
component.items = items
let createNewVal
component.createNew.subscribe((v) => (createNewVal = v))
expect(component.allowCreateNew).toBeTruthy()
component.onSearch({ term: 'foo' })
component.addItem(undefined)
expect(createNewVal).toEqual('foo')
component.addItem('bar')
expect(createNewVal).toEqual('bar')
component.onSearch({ term: 'baz' })
component.clickNew()
expect(createNewVal).toEqual('baz')
})
it('should clear search term on blur after delay', fakeAsync(() => {
const clearSpy = jest.spyOn(component, 'clearLastSearchTerm')
component.onBlur()
tick(3000)
expect(clearSpy).toHaveBeenCalled()
}))
})

View File

@@ -0,0 +1,140 @@
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import {
FormsModule,
ReactiveFormsModule,
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { TagsComponent } from './tags.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_ALL,
} from 'src/app/data/matching-model'
import { NgSelectModule } from '@ng-select/ng-select'
import { RouterTestingModule } from '@angular/router/testing'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { of } from 'rxjs'
import { TagService } from 'src/app/services/rest/tag.service'
import {
NgbModal,
NgbModalModule,
NgbModalRef,
} from '@ng-bootstrap/ng-bootstrap'
const tags: PaperlessTag[] = [
{
id: 1,
name: 'Tag1',
is_inbox_tag: false,
matching_algorithm: DEFAULT_MATCHING_ALGORITHM,
},
{
id: 2,
name: 'Tag2',
is_inbox_tag: true,
matching_algorithm: MATCH_ALL,
match: 'str',
},
{
id: 10,
name: 'Tag10',
is_inbox_tag: false,
matching_algorithm: DEFAULT_MATCHING_ALGORITHM,
},
]
describe('TagsComponent', () => {
let component: TagsComponent
let fixture: ComponentFixture<TagsComponent>
let input: HTMLInputElement
let modalService: NgbModal
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [TagsComponent],
providers: [
{
provide: TagService,
useValue: {
listAll: () => of(tags),
},
},
],
imports: [
FormsModule,
ReactiveFormsModule,
NgSelectModule,
RouterTestingModule,
HttpClientTestingModule,
NgbModalModule,
],
}).compileComponents()
modalService = TestBed.inject(NgbModal)
fixture = TestBed.createComponent(TagsComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
window.PointerEvent = MouseEvent as any
})
it('should support suggestions', () => {
expect(component.value).toBeUndefined()
component.value = []
component.tags = tags
component.suggestions = [1, 2]
fixture.detectChanges()
const suggestionAnchor: HTMLAnchorElement =
fixture.nativeElement.querySelector('a')
suggestionAnchor.click()
expect(component.value).toEqual([1])
})
it('should support create new and open a modal', () => {
let activeInstances: NgbModalRef[]
modalService.activeInstances.subscribe((v) => (activeInstances = v))
component.createTag('foo')
expect(modalService.hasOpenModals()).toBeTruthy()
expect(activeInstances[0].componentInstance.object.name).toEqual('foo')
})
it('should support create new using last search term and open a modal', () => {
let activeInstances: NgbModalRef[]
modalService.activeInstances.subscribe((v) => (activeInstances = v))
component.onSearch({ term: 'bar' })
component.createTag()
expect(modalService.hasOpenModals()).toBeTruthy()
expect(activeInstances[0].componentInstance.object.name).toEqual('bar')
})
it('should clear search term on blur after delay', fakeAsync(() => {
const clearSpy = jest.spyOn(component, 'clearLastSearchTerm')
component.onBlur()
tick(3000)
expect(clearSpy).toHaveBeenCalled()
}))
it('support remove tags', () => {
component.tags = tags
component.value = [1, 2]
component.removeTag(new PointerEvent('point'), 2)
expect(component.value).toEqual([1])
component.disabled = true
component.removeTag(new PointerEvent('point'), 1)
expect(component.value).toEqual([1])
})
it('should get tags', () => {
expect(component.getTag(2)).toBeNull()
component.tags = tags
expect(component.getTag(2)).toEqual(tags[1])
expect(component.getTag(4)).toBeUndefined()
})
})

View File

@@ -11,6 +11,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
import { TagService } from 'src/app/services/rest/tag.service'
import { EditDialogMode } from '../../edit-dialog/edit-dialog.component'
@Component({
providers: [
@@ -105,7 +106,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
var modal = this.modalService.open(TagEditDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.dialogMode = 'create'
modal.componentInstance.dialogMode = EditDialogMode.CREATE
if (name) modal.componentInstance.object = { name: name }
else if (this._lastSearchTerm)
modal.componentInstance.object = { name: this._lastSearchTerm }

View File

@@ -0,0 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
ReactiveFormsModule,
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { TextComponent } from './text.component'
describe('TextComponent', () => {
let component: TextComponent
let fixture: ComponentFixture<TextComponent>
let input: HTMLInputElement
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [TextComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents()
fixture = TestBed.createComponent(TextComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should support use of input field', () => {
expect(component.value).toBeUndefined()
// TODO: why doesnt this work?
// input.value = 'foo'
// input.dispatchEvent(new Event('change'))
// fixture.detectChanges()
// expect(component.value).toEqual('foo')
})
})

View File

@@ -0,0 +1,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { Title } from '@angular/platform-browser'
import { PageHeaderComponent } from './page-header.component'
import { environment } from 'src/environments/environment'
describe('PageHeaderComponent', () => {
let component: PageHeaderComponent
let fixture: ComponentFixture<PageHeaderComponent>
let titleService: Title
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [PageHeaderComponent],
providers: [],
imports: [],
}).compileComponents()
titleService = TestBed.inject(Title)
fixture = TestBed.createComponent(PageHeaderComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should display title + subtitle', () => {
component.title = 'Foo'
component.subTitle = 'Bar'
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('FooBar')
})
it('should set html title', () => {
const titleSpy = jest.spyOn(titleService, 'setTitle')
component.title = 'Foo Bar'
expect(titleSpy).toHaveBeenCalledWith(`Foo Bar - ${environment.appTitle}`)
})
})

View File

@@ -0,0 +1,90 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { PermissionsDialogComponent } from './permissions-dialog.component'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { UserService } from 'src/app/services/rest/user.service'
import { of } from 'rxjs'
import { PermissionsFormComponent } from '../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../input/select/select.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { PermissionsUserComponent } from '../input/permissions/permissions-user/permissions-user.component'
import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component'
const set_permissions = {
owner: 10,
set_permissions: {
view: {
users: [1],
groups: [],
},
edit: {
users: [1],
groups: [],
},
},
}
describe('PermissionsDialogComponent', () => {
let component: PermissionsDialogComponent
let fixture: ComponentFixture<PermissionsDialogComponent>
let modal: NgbActiveModal
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
PermissionsDialogComponent,
SafeHtmlPipe,
SelectComponent,
PermissionsFormComponent,
PermissionsUserComponent,
PermissionsGroupComponent,
],
providers: [
NgbActiveModal,
{
provide: UserService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
username: 'user1',
},
{
id: 10,
username: 'user10',
},
],
}),
},
},
],
imports: [
HttpClientTestingModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
NgbModule,
],
}).compileComponents()
modal = TestBed.inject(NgbActiveModal)
fixture = TestBed.createComponent(PermissionsDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should return permissions', () => {
component.form.get('permissions_form').setValue(set_permissions)
expect(component.permissions).toEqual(set_permissions)
})
it('should close modal on cancel', () => {
const closeSpy = jest.spyOn(modal, 'close')
component.cancelClicked()
expect(closeSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,157 @@
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of } from 'rxjs'
import { PermissionsService } from 'src/app/services/permissions.service'
import { UserService } from 'src/app/services/rest/user.service'
import {
OwnerFilterType,
PermissionsFilterDropdownComponent,
PermissionsSelectionModel,
} from './permissions-filter-dropdown.component'
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { SettingsService } from 'src/app/services/settings.service'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
const currentUserID = 13
describe('PermissionsFilterDropdownComponent', () => {
let component: PermissionsFilterDropdownComponent
let fixture: ComponentFixture<PermissionsFilterDropdownComponent>
let ownerFilterSetResult: PermissionsSelectionModel
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [
PermissionsFilterDropdownComponent,
ClearableBadgeComponent,
IfPermissionsDirective,
],
providers: [
{
provide: UserService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
username: 'user1',
},
{
id: 10,
username: 'user10',
},
],
}),
},
},
{
provide: PermissionsService,
useValue: {
currentUserCan: () => true,
},
},
{
provide: SettingsService,
useValue: {
currentUser: {
id: currentUserID,
},
},
},
],
imports: [
HttpClientTestingModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(PermissionsFilterDropdownComponent)
component = fixture.componentInstance
component.ownerFilterSet.subscribe(
(model) => (ownerFilterSetResult = model)
)
component.selectionModel = new PermissionsSelectionModel()
fixture.detectChanges()
})
it('should report is active', () => {
component.setFilter(OwnerFilterType.NONE)
expect(component.isActive).toBeFalsy()
component.setFilter(OwnerFilterType.OTHERS)
expect(component.isActive).toBeTruthy()
component.setFilter(OwnerFilterType.NONE)
component.selectionModel.hideUnowned = true
expect(component.isActive).toBeTruthy()
})
it('should support reset', () => {
component.setFilter(OwnerFilterType.OTHERS)
expect(component.selectionModel.ownerFilter).not.toEqual(
OwnerFilterType.NONE
)
component.reset()
expect(component.selectionModel.ownerFilter).toEqual(OwnerFilterType.NONE)
})
it('should toggle owner filter type when users selected', () => {
component.selectionModel.ownerFilter = OwnerFilterType.NONE
// this would normally be done by select component
component.selectionModel.includeUsers = [12]
component.onUserSelect()
expect(component.selectionModel.ownerFilter).toEqual(OwnerFilterType.OTHERS)
// this would normally be done by select component
component.selectionModel.includeUsers = null
component.onUserSelect()
expect(component.selectionModel.ownerFilter).toEqual(OwnerFilterType.NONE)
})
it('should emit a selection model depending on the type of owner filter set', () => {
component.selectionModel.ownerFilter = OwnerFilterType.NONE
component.setFilter(OwnerFilterType.SELF)
expect(ownerFilterSetResult).toEqual({
excludeUsers: [],
hideUnowned: false,
includeUsers: [],
ownerFilter: OwnerFilterType.SELF,
userID: currentUserID,
})
component.setFilter(OwnerFilterType.NOT_SELF)
expect(ownerFilterSetResult).toEqual({
excludeUsers: [currentUserID],
hideUnowned: false,
includeUsers: [],
ownerFilter: OwnerFilterType.NOT_SELF,
userID: null,
})
component.setFilter(OwnerFilterType.NONE)
expect(ownerFilterSetResult).toEqual({
excludeUsers: [],
hideUnowned: false,
includeUsers: [],
ownerFilter: OwnerFilterType.NONE,
userID: null,
})
component.setFilter(OwnerFilterType.UNOWNED)
expect(ownerFilterSetResult).toEqual({
excludeUsers: [],
hideUnowned: false,
includeUsers: [],
ownerFilter: OwnerFilterType.UNOWNED,
userID: null,
})
})
})

View File

@@ -0,0 +1,96 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { PermissionsSelectComponent } from './permissions-select.component'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import {
PermissionAction,
PermissionType,
} from 'src/app/services/permissions.service'
import { By } from '@angular/platform-browser'
const permissions = [
'add_document',
'view_document',
'change_document',
'delete_document',
'change_tag',
'view_documenttype',
]
const inheritedPermissions = ['change_tag', 'view_documenttype']
describe('PermissionsSelectComponent', () => {
let component: PermissionsSelectComponent
let fixture: ComponentFixture<PermissionsSelectComponent>
let permissionsChangeResult: Permissions
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [PermissionsSelectComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule, NgbModule],
}).compileComponents()
fixture = TestBed.createComponent(PermissionsSelectComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
component.registerOnChange((r) => (permissionsChangeResult = r))
fixture.detectChanges()
})
it('should create controls for all PermissionType and PermissionAction', () => {
expect(Object.values(component.form.controls)).toHaveLength(
Object.keys(PermissionType).length
)
for (var type in component.form.controls) {
expect(
Object.values(component.form.controls[type].controls)
).toHaveLength(Object.keys(PermissionAction).length)
}
// coverage
component.registerOnTouched(() => {})
component.setDisabledState(true)
})
it('should allow toggle all on / off', () => {
component.ngOnInit()
expect(component.typesWithAllActions.values).toHaveLength(0)
component.toggleAll({ target: { checked: true } }, 'Tag')
expect(component.typesWithAllActions).toContain('Tag')
component.toggleAll({ target: { checked: false } }, 'Tag')
expect(component.typesWithAllActions.values).toHaveLength(0)
})
it('should update on permissions set', () => {
component.ngOnInit()
component.writeValue(permissions)
expect(permissionsChangeResult).toEqual(permissions)
expect(component.typesWithAllActions).toContain('Document')
})
it('should update checkboxes on permissions set', () => {
component.ngOnInit()
component.writeValue(permissions)
fixture.detectChanges()
const input1 = fixture.debugElement.query(By.css('input#Document_Add'))
expect(input1.nativeElement.checked).toBeTruthy()
const input2 = fixture.debugElement.query(By.css('input#Tag_Change'))
expect(input2.nativeElement.checked).toBeTruthy()
})
it('disable checkboxes when permissions are inherited', () => {
component.ngOnInit()
component.inheritedPermissions = inheritedPermissions
expect(component.isInherited('Document', 'Add')).toBeFalsy()
expect(component.isInherited('Document')).toBeFalsy()
expect(component.isInherited('Tag', 'Change')).toBeTruthy()
const input1 = fixture.debugElement.query(By.css('input#Document_Add'))
expect(input1.nativeElement.disabled).toBeFalsy()
const input2 = fixture.debugElement.query(By.css('input#Tag_Change'))
expect(input2.nativeElement.disabled).toBeTruthy()
})
})

View File

@@ -0,0 +1,31 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { SelectComponent } from '../input/select/select.component'
import { SelectDialogComponent } from './select-dialog.component'
describe('SelectDialogComponent', () => {
let component: SelectDialogComponent
let fixture: ComponentFixture<SelectDialogComponent>
let modal: NgbActiveModal
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [SelectDialogComponent, SelectComponent],
providers: [NgbActiveModal],
imports: [NgSelectModule, FormsModule, ReactiveFormsModule],
}).compileComponents()
modal = TestBed.inject(NgbActiveModal)
fixture = TestBed.createComponent(SelectDialogComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should close modal on cancel', () => {
const closeSpy = jest.spyOn(modal, 'close')
component.cancelClicked()
expect(closeSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,51 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TagComponent } from './tag.component'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { By } from '@angular/platform-browser'
const tag: PaperlessTag = {
id: 1,
color: '#ff0000',
name: 'Tag1',
}
describe('TagComponent', () => {
let component: TagComponent
let fixture: ComponentFixture<TagComponent>
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [TagComponent],
providers: [],
imports: [],
}).compileComponents()
fixture = TestBed.createComponent(TagComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should create tag with background color', () => {
component.tag = tag
fixture.detectChanges()
expect(
fixture.debugElement.query(By.css('span')).nativeElement.style
.backgroundColor
).toEqual('rgb(255, 0, 0)')
})
it('should handle private tags', () => {
expect(
fixture.debugElement.query(By.css('span')).nativeElement.textContent
).toEqual('Private')
})
it('should support clickable option', () => {
component.tag = tag
fixture.detectChanges()
expect(fixture.debugElement.query(By.css('a.badge'))).toBeNull()
component.clickable = true
fixture.detectChanges()
expect(fixture.debugElement.query(By.css('a.badge'))).not.toBeNull()
})
})

View File

@@ -0,0 +1,94 @@
import {
TestBed,
discardPeriodicTasks,
fakeAsync,
flush,
} from '@angular/core/testing'
import { ToastService } from 'src/app/services/toast.service'
import { ToastsComponent } from './toasts.component'
import { ComponentFixture } from '@angular/core/testing'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { of } from 'rxjs'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
describe('ToastsComponent', () => {
let component: ToastsComponent
let fixture: ComponentFixture<ToastsComponent>
let toastService: ToastService
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ToastsComponent],
imports: [HttpClientTestingModule, NgbModule],
providers: [
{
provide: ToastService,
useValue: {
getToasts: () =>
of([
{
title: 'Title',
content: 'content',
delay: 5000,
},
{
title: 'Error',
content: 'Error content',
delay: 5000,
error: new Error('Error message'),
},
]),
},
},
],
}).compileComponents()
fixture = TestBed.createComponent(ToastsComponent)
component = fixture.componentInstance
toastService = TestBed.inject(ToastService)
fixture.detectChanges()
})
it('should call getToasts and return toasts', fakeAsync(() => {
const spy = jest.spyOn(toastService, 'getToasts').mockReset()
component.ngOnInit()
fixture.detectChanges()
expect(spy).toHaveBeenCalled()
expect(component.toasts).toContainEqual({
title: 'Title',
content: 'content',
delay: 5000,
})
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
it('should show a toast', fakeAsync(() => {
component.ngOnInit()
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('Title')
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
it('should show an error if given with toast', fakeAsync(() => {
component.ngOnInit()
fixture.detectChanges()
expect(fixture.nativeElement.querySelector('details')).not.toBeNull()
expect(fixture.nativeElement.textContent).toContain('Error message')
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
})

View File

@@ -15,7 +15,7 @@ export class ToastsComponent implements OnInit, OnDestroy {
toasts: Toast[] = []
ngOnDestroy(): void {
this.subscription.unsubscribe()
this.subscription?.unsubscribe()
}
ngOnInit(): void {