@if (customAppTitle?.length) {
{{customAppTitle}}
diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts
index 58aa029ee..fe377cc70 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts
+++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts
@@ -493,12 +493,17 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
expect(changedResult.getExcludedItems()).toEqual(items)
}))
- it('FilterableDropdownSelectionModel should sort items by state', () => {
- component.items = items
+ it('selection model should sort items by state', () => {
+ component.items = items.concat([{ id: null, name: 'Null B' }])
component.selectionModel = selectionModel
selectionModel.toggle(items[1].id)
selectionModel.apply()
- expect(selectionModel.itemsSorted).toEqual([nullItem, items[1], items[0]])
+ expect(selectionModel.itemsSorted).toEqual([
+ nullItem,
+ { id: null, name: 'Null B' },
+ items[1],
+ items[0],
+ ])
})
it('should set support create, keep open model and call createRef method', fakeAsync(() => {
@@ -542,4 +547,34 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
tick(300)
expect(createSpy).toHaveBeenCalled()
}))
+
+ it('should exclude item and trigger change event', () => {
+ const id = 1
+ const state = ToggleableItemState.Selected
+ component.selectionModel = selectionModel
+ component.manyToOne = true
+ component.selectionModel.singleSelect = true
+ component.selectionModel.intersection = Intersection.Include
+ component.selectionModel['temporarySelectionStates'].set(id, state)
+ const changedSpy = jest.spyOn(component.selectionModel.changed, 'next')
+ component.selectionModel.exclude(id)
+ expect(component.selectionModel.temporaryLogicalOperator).toBe(
+ LogicalOperator.And
+ )
+ expect(component.selectionModel['temporarySelectionStates'].get(id)).toBe(
+ ToggleableItemState.Excluded
+ )
+ expect(component.selectionModel['temporarySelectionStates'].size).toBe(1)
+ expect(changedSpy).toHaveBeenCalled()
+ })
+
+ it('should initialize selection states and apply changes', () => {
+ selectionModel.items = items
+ const map = new Map()
+ map.set(1, ToggleableItemState.Selected)
+ map.set(2, ToggleableItemState.Excluded)
+ selectionModel.init(map)
+ expect(selectionModel.getSelectedItems()).toEqual([items[0]])
+ expect(selectionModel.getExcludedItems()).toEqual([items[1]])
+ })
})
diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
index bb1a9da27..4f39d32c3 100644
--- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
+++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts
@@ -275,7 +275,7 @@ export class FilterableDropdownSelectionModel {
)
}
- init(map) {
+ init(map: Map) {
this.temporarySelectionStates = map
this.apply()
}
diff --git a/src-ui/src/app/components/common/input/select/select.component.spec.ts b/src-ui/src/app/components/common/input/select/select.component.spec.ts
index e9eee1648..79eec16e8 100644
--- a/src-ui/src/app/components/common/input/select/select.component.spec.ts
+++ b/src-ui/src/app/components/common/input/select/select.component.spec.ts
@@ -118,4 +118,18 @@ describe('SelectComponent', () => {
tick(3000)
expect(clearSpy).toHaveBeenCalled()
}))
+
+ it('should emit filtered documents', () => {
+ component.value = 10
+ component.items = items
+ const emitSpy = jest.spyOn(component.filterDocuments, 'emit')
+ component.onFilterDocuments()
+ expect(emitSpy).toHaveBeenCalledWith([items[2]])
+ })
+
+ it('should return the correct filter button title', () => {
+ component.title = 'Tag'
+ const expectedTitle = `Filter documents with this ${component.title}`
+ expect(component.filterButtonTitle).toEqual(expectedTitle)
+ })
})
diff --git a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts
index af321ab9e..f08fed4f8 100644
--- a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts
+++ b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts
@@ -169,4 +169,12 @@ describe('TagsComponent', () => {
expect(component.getTag(2)).toEqual(tags[1])
expect(component.getTag(4)).toBeUndefined()
})
+
+ it('should emit filtered documents', () => {
+ component.value = [10]
+ component.tags = tags
+ const emitSpy = jest.spyOn(component.filterDocuments, 'emit')
+ component.onFilterDocuments()
+ expect(emitSpy).toHaveBeenCalledWith([tags[2]])
+ })
})
diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts
index 5ecc116c2..172239dbb 100644
--- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts
+++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts
@@ -119,6 +119,8 @@ describe('UploadFileWidgetComponent', () => {
const processingStatus = new FileStatus()
processingStatus.phase = FileStatusPhase.WORKING
expect(component.getStatusColor(processingStatus)).toEqual('primary')
+ processingStatus.phase = FileStatusPhase.UPLOADING
+ expect(component.getStatusColor(processingStatus)).toEqual('primary')
const failedStatus = new FileStatus()
failedStatus.phase = FileStatusPhase.FAILED
expect(component.getStatusColor(failedStatus)).toEqual('danger')
diff --git a/src-ui/src/app/guards/dirty-form.guard.spec.ts b/src-ui/src/app/guards/dirty-form.guard.spec.ts
index 24ee24f74..c5c473b27 100644
--- a/src-ui/src/app/guards/dirty-form.guard.spec.ts
+++ b/src-ui/src/app/guards/dirty-form.guard.spec.ts
@@ -17,6 +17,7 @@ describe('DirtyFormGuard', () => {
let guard: DirtyFormGuard
let component: DirtyComponent
let route: ActivatedRoute
+ let modalService: NgbModal
beforeEach(() => {
TestBed.configureTestingModule({
@@ -37,6 +38,7 @@ describe('DirtyFormGuard', () => {
guard = TestBed.inject(DirtyFormGuard)
route = TestBed.inject(ActivatedRoute)
+ modalService = TestBed.inject(NgbModal)
const fixture = TestBed.createComponent(GenericDirtyComponent)
component = fixture.componentInstance
@@ -57,9 +59,14 @@ describe('DirtyFormGuard', () => {
component.isDirty$ = true
const confirmSpy = jest.spyOn(guard, 'confirmChanges')
const canDeactivate = guard.canDeactivate(component, route.snapshot)
+ let modal
+ modalService.activeInstances.subscribe((instances) => {
+ modal = instances[0]
+ })
canDeactivate.subscribe()
expect(canDeactivate).toHaveProperty('source') // Observable
expect(confirmSpy).toHaveBeenCalled()
+ modal.componentInstance.confirmClicked.next()
})
})
diff --git a/src-ui/src/app/services/open-documents.service.spec.ts b/src-ui/src/app/services/open-documents.service.spec.ts
index 3c8e29edd..69d2a4a37 100644
--- a/src-ui/src/app/services/open-documents.service.spec.ts
+++ b/src-ui/src/app/services/open-documents.service.spec.ts
@@ -108,6 +108,7 @@ describe('OpenDocumentsService', () => {
})
it('should close documents', () => {
+ openDocumentsService.closeDocument({ id: 999 } as any)
subscriptions.push(
openDocumentsService.openDocument(documents[0]).subscribe()
)
@@ -128,15 +129,21 @@ describe('OpenDocumentsService', () => {
subscriptions.push(
openDocumentsService.openDocument(documents[0]).subscribe()
)
+ openDocumentsService.setDirty({ id: 999 }, true) // coverage
openDocumentsService.setDirty(documents[0], false)
expect(openDocumentsService.hasDirty()).toBeFalsy()
openDocumentsService.setDirty(documents[0], true)
expect(openDocumentsService.hasDirty()).toBeTruthy()
+ let openModal
+ modalService.activeInstances.subscribe((instances) => {
+ openModal = instances[0]
+ })
const modalSpy = jest.spyOn(modalService, 'open')
subscriptions.push(
openDocumentsService.closeDocument(documents[0]).subscribe()
)
expect(modalSpy).toHaveBeenCalled()
+ openModal.componentInstance.confirmClicked.next()
})
it('should allow set dirty status, warn on closeAll', () => {
@@ -148,9 +155,14 @@ describe('OpenDocumentsService', () => {
)
openDocumentsService.setDirty(documents[0], true)
expect(openDocumentsService.hasDirty()).toBeTruthy()
+ let openModal
+ modalService.activeInstances.subscribe((instances) => {
+ openModal = instances[0]
+ })
const modalSpy = jest.spyOn(modalService, 'open')
subscriptions.push(openDocumentsService.closeAll().subscribe())
expect(modalSpy).toHaveBeenCalled()
+ openModal.componentInstance.confirmClicked.next()
})
it('should load open documents from localStorage', () => {
diff --git a/src-ui/src/app/services/rest/mail-account.service.spec.ts b/src-ui/src/app/services/rest/mail-account.service.spec.ts
index 80a66f28b..64974d834 100644
--- a/src-ui/src/app/services/rest/mail-account.service.spec.ts
+++ b/src-ui/src/app/services/rest/mail-account.service.spec.ts
@@ -58,12 +58,25 @@ describe(`Additional service tests for MailAccountService`, () => {
it('should support patchMany', () => {
subscription = service.patchMany(mail_accounts).subscribe()
mail_accounts.forEach((mail_account) => {
- const reqs = httpTestingController.match(
+ const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${mail_account.id}/`
)
- expect(reqs).toHaveLength(1)
- expect(reqs[0].request.method).toEqual('PATCH')
+ expect(req.request.method).toEqual('PATCH')
+ req.flush(mail_account)
})
+ httpTestingController.expectOne(
+ `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
+ )
+ })
+
+ it('should support reload', () => {
+ service['reload']()
+ const req = httpTestingController.expectOne(
+ `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
+ )
+ expect(req.request.method).toEqual('GET')
+ req.flush({ results: mail_accounts })
+ expect(service.allAccounts).toEqual(mail_accounts)
})
beforeEach(() => {
diff --git a/src-ui/src/app/services/rest/mail-rule.service.spec.ts b/src-ui/src/app/services/rest/mail-rule.service.spec.ts
index cc5ac9928..ea84e8b86 100644
--- a/src-ui/src/app/services/rest/mail-rule.service.spec.ts
+++ b/src-ui/src/app/services/rest/mail-rule.service.spec.ts
@@ -76,12 +76,26 @@ describe(`Additional service tests for MailRuleService`, () => {
it('should support patchMany', () => {
subscription = service.patchMany(mail_rules).subscribe()
mail_rules.forEach((mail_rule) => {
- const reqs = httpTestingController.match(
+ const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${mail_rule.id}/`
)
- expect(reqs).toHaveLength(1)
- expect(reqs[0].request.method).toEqual('PATCH')
+ expect(req.request.method).toEqual('PATCH')
+ req.flush(mail_rule)
})
+ const reloadReq = httpTestingController.expectOne(
+ `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
+ )
+ reloadReq.flush({ results: mail_rules })
+ })
+
+ it('should support reload', () => {
+ service['reload']()
+ const req = httpTestingController.expectOne(
+ `${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
+ )
+ expect(req.request.method).toEqual('GET')
+ req.flush({ results: mail_rules })
+ expect(service.allRules).toEqual(mail_rules)
})
beforeEach(() => {
diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss
index 1b982b2b0..bc1efaf23 100644
--- a/src-ui/src/styles.scss
+++ b/src-ui/src/styles.scss
@@ -262,7 +262,7 @@ a.btn-link:focus-visible,
}
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
- background-color: var(--pngx-bg-darker) !important;
+ background-color: var(--pngx-bg-alt) !important;
color: var(--pngx-body-color-accent) !important;
}
@@ -439,7 +439,7 @@ ul.pagination {
color: var(--bs-body-color);
&:hover, &:focus {
- background-color: var(--pngx-bg-darker);
+ background-color: var(--pngx-bg-alt);
color: var(--bs-body-color);
}
diff --git a/src/paperless/views.py b/src/paperless/views.py
index 1151ceed5..974830d83 100644
--- a/src/paperless/views.py
+++ b/src/paperless/views.py
@@ -14,7 +14,7 @@ from rest_framework.authtoken.models import Token
from rest_framework.filters import OrderingFilter
from rest_framework.generics import GenericAPIView
from rest_framework.pagination import PageNumberPagination
-from rest_framework.permissions import DjangoObjectPermissions
+from rest_framework.permissions import DjangoModelPermissions
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
@@ -171,7 +171,7 @@ class ApplicationConfigurationViewSet(ModelViewSet):
queryset = ApplicationConfiguration.objects
serializer_class = ApplicationConfigurationSerializer
- permission_classes = (IsAuthenticated, DjangoObjectPermissions)
+ permission_classes = (IsAuthenticated, DjangoModelPermissions)
class DisconnectSocialAccountView(GenericAPIView):