mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00

Adds custom fields of certain data types, attachable to documents and searchable Co-Authored-By: Trenton H <797416+stumpylog@users.noreply.github.com>
367 lines
13 KiB
TypeScript
367 lines
13 KiB
TypeScript
import {
|
|
HttpClientTestingModule,
|
|
HttpTestingController,
|
|
} from '@angular/common/http/testing'
|
|
import { AppFrameComponent } from './app-frame.component'
|
|
import {
|
|
ComponentFixture,
|
|
TestBed,
|
|
fakeAsync,
|
|
tick,
|
|
} from '@angular/core/testing'
|
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
|
import { BrowserModule } from '@angular/platform-browser'
|
|
import { RouterTestingModule } from '@angular/router/testing'
|
|
import { SettingsService } from 'src/app/services/settings.service'
|
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
|
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
|
|
import { RemoteVersionService } from 'src/app/services/rest/remote-version.service'
|
|
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
|
import { of, throwError } from 'rxjs'
|
|
import { ToastService } from 'src/app/services/toast.service'
|
|
import { environment } from 'src/environments/environment'
|
|
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
|
import { ActivatedRoute, Router } from '@angular/router'
|
|
import { DocumentDetailComponent } from '../document-detail/document-detail.component'
|
|
import { SearchService } from 'src/app/services/rest/search.service'
|
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
|
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
|
|
import { routes } from 'src/app/app-routing.module'
|
|
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
|
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop'
|
|
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
|
|
|
|
const saved_views = [
|
|
{
|
|
name: 'Saved View 0',
|
|
id: 0,
|
|
show_on_dashboard: true,
|
|
show_in_sidebar: true,
|
|
sort_field: 'name',
|
|
sort_reverse: true,
|
|
filter_rules: [],
|
|
},
|
|
{
|
|
name: 'Saved View 1',
|
|
id: 1,
|
|
show_on_dashboard: false,
|
|
show_in_sidebar: false,
|
|
sort_field: 'name',
|
|
sort_reverse: true,
|
|
filter_rules: [],
|
|
},
|
|
{
|
|
name: 'Saved View 2',
|
|
id: 2,
|
|
show_on_dashboard: true,
|
|
show_in_sidebar: true,
|
|
sort_field: 'name',
|
|
sort_reverse: true,
|
|
filter_rules: [],
|
|
},
|
|
{
|
|
name: 'Saved View 3',
|
|
id: 3,
|
|
show_on_dashboard: true,
|
|
show_in_sidebar: true,
|
|
sort_field: 'name',
|
|
sort_reverse: true,
|
|
filter_rules: [],
|
|
},
|
|
]
|
|
const document = { id: 2, title: 'Hello world' }
|
|
|
|
describe('AppFrameComponent', () => {
|
|
let component: AppFrameComponent
|
|
let fixture: ComponentFixture<AppFrameComponent>
|
|
let httpTestingController: HttpTestingController
|
|
let settingsService: SettingsService
|
|
let permissionsService: PermissionsService
|
|
let remoteVersionService: RemoteVersionService
|
|
let toastService: ToastService
|
|
let openDocumentsService: OpenDocumentsService
|
|
let searchService: SearchService
|
|
let documentListViewService: DocumentListViewService
|
|
let router: Router
|
|
let savedViewSpy
|
|
|
|
beforeEach(async () => {
|
|
TestBed.configureTestingModule({
|
|
declarations: [AppFrameComponent, IfPermissionsDirective],
|
|
imports: [
|
|
HttpClientTestingModule,
|
|
BrowserModule,
|
|
RouterTestingModule.withRoutes(routes),
|
|
NgbModule,
|
|
FormsModule,
|
|
ReactiveFormsModule,
|
|
DragDropModule,
|
|
],
|
|
providers: [
|
|
SettingsService,
|
|
{
|
|
provide: SavedViewService,
|
|
useValue: {
|
|
initialize: () => {},
|
|
listAll: () =>
|
|
of({
|
|
all: [saved_views.map((v) => v.id)],
|
|
count: saved_views.length,
|
|
results: saved_views,
|
|
}),
|
|
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
|
},
|
|
},
|
|
PermissionsService,
|
|
RemoteVersionService,
|
|
IfPermissionsDirective,
|
|
ToastService,
|
|
OpenDocumentsService,
|
|
SearchService,
|
|
{
|
|
provide: ActivatedRoute,
|
|
useValue: {
|
|
firstChild: {
|
|
component: DocumentDetailComponent,
|
|
},
|
|
snapshot: {
|
|
firstChild: {
|
|
component: DocumentDetailComponent,
|
|
params: {
|
|
id: document.id,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
PermissionsGuard,
|
|
],
|
|
}).compileComponents()
|
|
|
|
settingsService = TestBed.inject(SettingsService)
|
|
const savedViewService = TestBed.inject(SavedViewService)
|
|
permissionsService = TestBed.inject(PermissionsService)
|
|
remoteVersionService = TestBed.inject(RemoteVersionService)
|
|
toastService = TestBed.inject(ToastService)
|
|
openDocumentsService = TestBed.inject(OpenDocumentsService)
|
|
searchService = TestBed.inject(SearchService)
|
|
documentListViewService = TestBed.inject(DocumentListViewService)
|
|
router = TestBed.inject(Router)
|
|
|
|
jest
|
|
.spyOn(settingsService, 'displayName', 'get')
|
|
.mockReturnValue('Hello World')
|
|
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
|
|
|
savedViewSpy = jest.spyOn(savedViewService, 'initialize')
|
|
|
|
fixture = TestBed.createComponent(AppFrameComponent)
|
|
component = fixture.componentInstance
|
|
|
|
httpTestingController = TestBed.inject(HttpTestingController)
|
|
|
|
fixture.detectChanges()
|
|
})
|
|
|
|
it('should initialize the saved view service', () => {
|
|
expect(savedViewSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should check for update if enabled', () => {
|
|
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
|
updateCheckSpy.mockImplementation(() => {
|
|
return of({
|
|
version: 'v100.0',
|
|
update_available: true,
|
|
})
|
|
})
|
|
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, true)
|
|
component.ngOnInit()
|
|
expect(updateCheckSpy).toHaveBeenCalled()
|
|
fixture.detectChanges()
|
|
expect(fixture.nativeElement.textContent).toContain('Update available')
|
|
})
|
|
|
|
it('should check not for update if disabled', () => {
|
|
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
|
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
|
component.ngOnInit()
|
|
fixture.detectChanges()
|
|
expect(updateCheckSpy).not.toHaveBeenCalled()
|
|
expect(fixture.nativeElement.textContent).not.toContain('Update available')
|
|
})
|
|
|
|
it('should check for update if was disabled and then enabled', () => {
|
|
const updateCheckSpy = jest.spyOn(remoteVersionService, 'checkForUpdates')
|
|
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
|
component.setUpdateChecking(true)
|
|
fixture.detectChanges()
|
|
expect(updateCheckSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should show error on toggle update checking if store settings fails', () => {
|
|
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
|
settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, false)
|
|
component.setUpdateChecking(true)
|
|
httpTestingController
|
|
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
|
.flush('error', {
|
|
status: 500,
|
|
statusText: 'error',
|
|
})
|
|
expect(toastSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should support toggling slim sidebar and saving', fakeAsync(() => {
|
|
const saveSettingSpy = jest.spyOn(settingsService, 'set')
|
|
expect(component.slimSidebarEnabled).toBeFalsy()
|
|
expect(component.slimSidebarAnimating).toBeFalsy()
|
|
component.toggleSlimSidebar()
|
|
expect(component.slimSidebarAnimating).toBeTruthy()
|
|
tick(200)
|
|
expect(component.slimSidebarAnimating).toBeFalsy()
|
|
expect(component.slimSidebarEnabled).toBeTruthy()
|
|
expect(saveSettingSpy).toHaveBeenCalledWith(
|
|
SETTINGS_KEYS.SLIM_SIDEBAR,
|
|
true
|
|
)
|
|
}))
|
|
|
|
it('should show error on toggle slim sidebar if store settings fails', () => {
|
|
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
|
component.toggleSlimSidebar()
|
|
httpTestingController
|
|
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
|
.flush('error', {
|
|
status: 500,
|
|
statusText: 'error',
|
|
})
|
|
expect(toastSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should support collapsable menu', () => {
|
|
const button: HTMLButtonElement = (
|
|
fixture.nativeElement as HTMLDivElement
|
|
).querySelector('button[data-toggle=collapse]')
|
|
button.dispatchEvent(new MouseEvent('click'))
|
|
expect(component.isMenuCollapsed).toBeFalsy()
|
|
component.closeMenu()
|
|
expect(component.isMenuCollapsed).toBeTruthy()
|
|
})
|
|
|
|
it('should support close document & navigate on close current doc', () => {
|
|
const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument')
|
|
closeSpy.mockReturnValue(of(true))
|
|
const routerSpy = jest.spyOn(router, 'navigate')
|
|
component.closeDocument(document)
|
|
expect(closeSpy).toHaveBeenCalledWith(document)
|
|
expect(routerSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should support close all documents & navigate on close current doc', () => {
|
|
const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll')
|
|
closeAllSpy.mockReturnValue(of(true))
|
|
const routerSpy = jest.spyOn(router, 'navigate')
|
|
component.closeAll()
|
|
expect(closeAllSpy).toHaveBeenCalled()
|
|
expect(routerSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should close all documents on logout', () => {
|
|
const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll')
|
|
component.onLogout()
|
|
expect(closeAllSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should warn before close if dirty documents', () => {
|
|
jest.spyOn(openDocumentsService, 'hasDirty').mockReturnValue(true)
|
|
expect(component.canDeactivate()).toBeFalsy()
|
|
})
|
|
|
|
it('should call autocomplete endpoint on input', fakeAsync(() => {
|
|
const autocompleteSpy = jest.spyOn(searchService, 'autocomplete')
|
|
component.searchAutoComplete(of('hello')).subscribe()
|
|
tick(250)
|
|
expect(autocompleteSpy).toHaveBeenCalled()
|
|
|
|
component.searchAutoComplete(of('hello world 1')).subscribe()
|
|
tick(250)
|
|
expect(autocompleteSpy).toHaveBeenCalled()
|
|
}))
|
|
|
|
it('should support reset search field', () => {
|
|
const resetSpy = jest.spyOn(component, 'resetSearchField')
|
|
const input = (fixture.nativeElement as HTMLDivElement).querySelector(
|
|
'input'
|
|
) as HTMLInputElement
|
|
input.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }))
|
|
expect(resetSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should support choosing a search item', () => {
|
|
expect(component.searchField.value).toEqual('')
|
|
component.itemSelected({ item: 'hello', preventDefault: () => true })
|
|
expect(component.searchField.value).toEqual('hello ')
|
|
component.itemSelected({ item: 'world', preventDefault: () => true })
|
|
expect(component.searchField.value).toEqual('hello world ')
|
|
})
|
|
|
|
it('should navigate via quickFilter on search', () => {
|
|
const str = 'hello world '
|
|
component.searchField.patchValue(str)
|
|
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
|
component.search()
|
|
expect(qfSpy).toHaveBeenCalledWith([
|
|
{
|
|
rule_type: FILTER_FULLTEXT_QUERY,
|
|
value: str.trim(),
|
|
},
|
|
])
|
|
})
|
|
|
|
it('should disable global dropzone on start drag + drop, re-enable after', () => {
|
|
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
|
|
component.onDragStart(null)
|
|
expect(settingsService.globalDropzoneEnabled).toBeFalsy()
|
|
component.onDragEnd(null)
|
|
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
|
|
})
|
|
|
|
it('should update saved view sorting on drag + drop, show info', () => {
|
|
const settingsSpy = jest.spyOn(settingsService, 'updateSidebarViewsSort')
|
|
const toastSpy = jest.spyOn(toastService, 'showInfo')
|
|
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
|
|
component.onDrop({ previousIndex: 0, currentIndex: 1 } as CdkDragDrop<
|
|
PaperlessSavedView[]
|
|
>)
|
|
expect(settingsSpy).toHaveBeenCalledWith([
|
|
saved_views[2],
|
|
saved_views[0],
|
|
saved_views[3],
|
|
])
|
|
expect(toastSpy).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should update saved view sorting on drag + drop, show error', () => {
|
|
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
|
|
if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return []
|
|
})
|
|
fixture.destroy()
|
|
fixture = TestBed.createComponent(AppFrameComponent)
|
|
component = fixture.componentInstance
|
|
fixture.detectChanges()
|
|
const toastSpy = jest.spyOn(toastService, 'showError')
|
|
jest
|
|
.spyOn(settingsService, 'storeSettings')
|
|
.mockReturnValue(throwError(() => new Error('unable to save')))
|
|
component.onDrop({ previousIndex: 0, currentIndex: 2 } as CdkDragDrop<
|
|
PaperlessSavedView[]
|
|
>)
|
|
expect(toastSpy).toHaveBeenCalled()
|
|
})
|
|
})
|