mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
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:
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
}))
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -1,4 +1,3 @@
|
||||
import { formatDate } from '@angular/common'
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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')
|
||||
})
|
||||
})
|
@@ -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
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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)
|
||||
}))
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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>
|
||||
|
@@ -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]])
|
||||
})
|
||||
})
|
@@ -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)
|
||||
) {
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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>
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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({
|
||||
|
@@ -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">
|
||||
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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">
|
||||
|
@@ -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 })
|
||||
})
|
||||
})
|
@@ -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">
|
||||
|
@@ -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)
|
||||
})
|
||||
})
|
@@ -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')
|
||||
})
|
||||
})
|
@@ -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: [] },
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
}))
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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 }
|
||||
|
@@ -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')
|
||||
})
|
||||
})
|
@@ -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}`)
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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,
|
||||
})
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
})
|
||||
})
|
51
src-ui/src/app/components/common/tag/tag.component.spec.ts
Normal file
51
src-ui/src/app/components/common/tag/tag.component.spec.ts
Normal 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()
|
||||
})
|
||||
})
|
@@ -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()
|
||||
}))
|
||||
})
|
@@ -15,7 +15,7 @@ export class ToastsComponent implements OnInit, OnDestroy {
|
||||
toasts: Toast[] = []
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe()
|
||||
this.subscription?.unsubscribe()
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
Reference in New Issue
Block a user