mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-08-09 20:33:18 +00:00
Remove zone dependency for tests
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import '@angular/localize/init'
|
||||
import { jest } from '@jest/globals'
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'
|
||||
import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'
|
||||
import { TextDecoder, TextEncoder } from 'node:util'
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
setupZoneTestEnv()
|
||||
setupZonelessTestEnv()
|
||||
}
|
||||
;(globalThis as any).TextEncoder = TextEncoder as unknown as {
|
||||
new (): TextEncoder
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
fakeAsync,
|
||||
TestBed,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { Router, RouterModule } from '@angular/router'
|
||||
import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
@@ -72,7 +67,8 @@ describe('AppComponent', () => {
|
||||
component = fixture.componentInstance
|
||||
})
|
||||
|
||||
it('should initialize the tour service & toggle class on body for styling', fakeAsync(() => {
|
||||
it('should initialize the tour service & toggle class on body for styling', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
fixture.detectChanges()
|
||||
const tourSpy = jest.spyOn(tourService, 'initialize')
|
||||
@@ -81,9 +77,10 @@ describe('AppComponent', () => {
|
||||
tourService.start()
|
||||
expect(document.body.classList).toContain('tour-active')
|
||||
tourService.end()
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(document.body.classList).not.toContain('tour-active')
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should display toast on document consumed with link if user has access', () => {
|
||||
const navigateSpy = jest.spyOn(router, 'navigate')
|
||||
|
||||
@@ -209,7 +209,7 @@ describe('SettingsComponent', () => {
|
||||
fixture.detectChanges()
|
||||
}
|
||||
|
||||
it('should support tabbed settings & change URL, prevent navigation if dirty confirmation rejected', () => {
|
||||
it('should support tabbed settings & change URL, prevent navigation if dirty confirmation rejected', async () => {
|
||||
completeSetup()
|
||||
const navigateSpy = jest.spyOn(router, 'navigate')
|
||||
const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavLink))
|
||||
@@ -217,16 +217,19 @@ describe('SettingsComponent', () => {
|
||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'documents'])
|
||||
tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions'])
|
||||
await fixture.whenStable()
|
||||
|
||||
const initSpy = jest.spyOn(component, 'initialize')
|
||||
component.isDirty = true // mock dirty
|
||||
navigateSpy.mockResolvedValueOnce(false) // nav rejected cause dirty
|
||||
tabButtons[0].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||
await fixture.whenStable()
|
||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'general'])
|
||||
expect(initSpy).not.toHaveBeenCalled()
|
||||
|
||||
navigateSpy.mockResolvedValueOnce(true) // nav accepted even though dirty
|
||||
tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||
await fixture.whenStable()
|
||||
expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions'])
|
||||
expect(initSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -60,10 +60,15 @@ describe('TrashComponent', () => {
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
router = TestBed.inject(Router)
|
||||
jest.spyOn(router, 'navigate').mockResolvedValue(true)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should call correct service method on reload', () => {
|
||||
jest.useFakeTimers()
|
||||
const trashSpy = jest.spyOn(trashService, 'getTrash')
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { DatePipe } from '@angular/common'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of, throwError } from 'rxjs'
|
||||
@@ -138,7 +133,8 @@ describe('UsersAndGroupsComponent', () => {
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith('Deleted user "user1"')
|
||||
})
|
||||
|
||||
it('should logout current user if password changed, after delay', fakeAsync(() => {
|
||||
it('should logout current user if password changed, after delay', () => {
|
||||
jest.useFakeTimers()
|
||||
completeSetup()
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
|
||||
@@ -151,11 +147,12 @@ describe('UsersAndGroupsComponent', () => {
|
||||
settingsService.currentUser.set(users[0]) // simulate logged in as same user
|
||||
editDialog.succeeded.emit(users[0])
|
||||
fixture.detectChanges()
|
||||
tick(2600)
|
||||
jest.advanceTimersByTime(2600)
|
||||
expect(navSpy).toHaveBeenCalledWith(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support edit / create group, show error if needed', () => {
|
||||
completeSetup()
|
||||
|
||||
@@ -4,12 +4,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
@@ -244,7 +239,8 @@ describe('AppFrameComponent', () => {
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support toggling slim sidebar and saving', fakeAsync(() => {
|
||||
it('should support toggling slim sidebar and saving', () => {
|
||||
jest.useFakeTimers()
|
||||
const saveSettingSpy = jest.spyOn(settingsService, 'set')
|
||||
settingsService.set(SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED, [])
|
||||
expect(component.slimSidebarEnabled).toBeFalsy()
|
||||
@@ -260,7 +256,7 @@ describe('AppFrameComponent', () => {
|
||||
).toEqual(['attributes'])
|
||||
requests[0].flush({ success: true })
|
||||
expect(component.slimSidebarAnimating()).toBeTruthy()
|
||||
tick(200)
|
||||
jest.advanceTimersByTime(200)
|
||||
expect(component.slimSidebarAnimating()).toBeFalsy()
|
||||
expect(component.slimSidebarEnabled).toBeTruthy()
|
||||
expect(saveSettingSpy).toHaveBeenCalledWith(
|
||||
@@ -271,7 +267,8 @@ describe('AppFrameComponent', () => {
|
||||
SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED,
|
||||
['attributes']
|
||||
)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should show error on toggle slim sidebar if store settings fails', () => {
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { ElementRef } from '@angular/core'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { Router } from '@angular/router'
|
||||
import {
|
||||
@@ -154,6 +149,7 @@ describe('GlobalSearchComponent', () => {
|
||||
|
||||
searchService = TestBed.inject(SearchService)
|
||||
router = TestBed.inject(Router)
|
||||
jest.spyOn(router, 'navigate').mockResolvedValue(true)
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
documentService = TestBed.inject(DocumentService)
|
||||
documentListViewService = TestBed.inject(DocumentListViewService)
|
||||
@@ -276,20 +272,22 @@ describe('GlobalSearchComponent', () => {
|
||||
expect(advancedSearchSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should search on query debounce', fakeAsync(() => {
|
||||
it('should search on query debounce', () => {
|
||||
jest.useFakeTimers()
|
||||
const query = 'test'
|
||||
const searchSpy = jest.spyOn(searchService, 'globalSearch')
|
||||
searchSpy.mockReturnValue(of({} as any))
|
||||
const dropdownOpenSpy = jest.spyOn(component.resultsDropdown, 'open')
|
||||
component.queryDebounce.next(query)
|
||||
tick(401)
|
||||
jest.advanceTimersByTime(401)
|
||||
expect(searchSpy).toHaveBeenCalledWith(query)
|
||||
expect(dropdownOpenSpy).toHaveBeenCalled()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support primary action', () => {
|
||||
const object = { id: 1 }
|
||||
const routerSpy = jest.spyOn(router, 'navigate')
|
||||
const routerSpy = jest.mocked(router.navigate)
|
||||
const modalSpy = jest.spyOn(modalService, 'open')
|
||||
|
||||
let modal: NgbModalRef
|
||||
|
||||
+7
-21
@@ -1,12 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
flush,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { Subject } from 'rxjs'
|
||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||
@@ -69,7 +63,7 @@ describe('ToastsDropdownComponent', () => {
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should call getToasts and return toasts', fakeAsync(() => {
|
||||
it('should call getToasts and return toasts', () => {
|
||||
toastsSubject.next(toasts)
|
||||
fixture.detectChanges()
|
||||
|
||||
@@ -79,29 +73,21 @@ describe('ToastsDropdownComponent', () => {
|
||||
content: 'foo bar',
|
||||
delay: 5000,
|
||||
})
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should show a toast', fakeAsync(() => {
|
||||
it('should show a toast', () => {
|
||||
toastsSubject.next(toasts)
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(fixture.nativeElement.textContent).toContain('foo bar')
|
||||
})
|
||||
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
|
||||
it('should toggle suppressPopupToasts', fakeAsync((finish) => {
|
||||
it('should toggle suppressPopupToasts', () => {
|
||||
fixture.detectChanges()
|
||||
toastsSubject.next(toasts)
|
||||
|
||||
const spy = jest.spyOn(toastService, 'suppressPopupToasts', 'set')
|
||||
component.onOpenChange(true)
|
||||
expect(spy).toHaveBeenCalledWith(true)
|
||||
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
+18
-17
@@ -1,11 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import {
|
||||
@@ -110,7 +105,8 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should support creating field, show error if necessary, then add', fakeAsync(() => {
|
||||
it('should support creating field, show error if necessary, then add', () => {
|
||||
jest.useFakeTimers()
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
@@ -134,11 +130,12 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
|
||||
// succeed
|
||||
editDialog.succeeded.emit(fields[0])
|
||||
tick(100)
|
||||
jest.advanceTimersByTime(100)
|
||||
expect(toastInfoSpy).toHaveBeenCalled()
|
||||
expect(getFieldsSpy).toHaveBeenCalled()
|
||||
expect(addFieldSpy).toHaveBeenCalled()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support creating field with name', () => {
|
||||
let modal: NgbModalRef
|
||||
@@ -150,12 +147,13 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
expect(editDialog.object.name).toEqual('Foo bar')
|
||||
})
|
||||
|
||||
it('should support arrow keyboard navigation', fakeAsync(() => {
|
||||
it('should support arrow keyboard navigation', () => {
|
||||
jest.useFakeTimers()
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
jest.advanceTimersByTime(100)
|
||||
const filterInputEl: HTMLInputElement =
|
||||
component.listFilterTextInput.nativeElement
|
||||
expect(document.activeElement).toEqual(filterInputEl)
|
||||
@@ -193,14 +191,16 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
||||
)
|
||||
expect(document.activeElement).toEqual(itemButtons[0])
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support arrow keyboard navigation after tab keyboard navigation', fakeAsync(() => {
|
||||
it('should support arrow keyboard navigation after tab keyboard navigation', () => {
|
||||
jest.useFakeTimers()
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
jest.advanceTimersByTime(100)
|
||||
const filterInputEl: HTMLInputElement =
|
||||
component.listFilterTextInput.nativeElement
|
||||
expect(document.activeElement).toEqual(filterInputEl)
|
||||
@@ -229,9 +229,10 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
||||
)
|
||||
expect(document.activeElement).toEqual(itemButtons[1])
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support enter keyboard navigation', fakeAsync(() => {
|
||||
it('should support enter keyboard navigation', () => {
|
||||
jest.spyOn(component, 'canCreateFields', 'get').mockReturnValue(true)
|
||||
const addFieldSpy = jest.spyOn(component, 'addField')
|
||||
const createFieldSpy = jest.spyOn(component, 'createField')
|
||||
@@ -250,5 +251,5 @@ describe('CustomFieldsDropdownComponent', () => {
|
||||
component.listFilterEnter()
|
||||
expect(createFieldSpy).not.toHaveBeenCalled()
|
||||
expect(addFieldSpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
+4
-9
@@ -1,11 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
fakeAsync,
|
||||
TestBed,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgSelectModule } from '@ng-select/ng-select'
|
||||
@@ -210,13 +205,13 @@ describe('CustomFieldsQueryDropdownComponent', () => {
|
||||
expect(component.name).toBe('test_title')
|
||||
})
|
||||
|
||||
it('should add a default atom on open', fakeAsync(() => {
|
||||
it('should add a default atom on open', async () => {
|
||||
expect(component.selectionModel.queries.length).toBe(0)
|
||||
component.onOpenChange(true)
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
await fixture.whenStable()
|
||||
expect(component.selectionModel.queries.length).toBe(1)
|
||||
}))
|
||||
})
|
||||
|
||||
describe('CustomFieldQueriesModel', () => {
|
||||
let model: CustomFieldQueriesModel
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { DatePipe } from '@angular/common'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
@@ -59,27 +54,32 @@ describe('DatesDropdownComponent', () => {
|
||||
expect(settingsSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support date input, emit change', fakeAsync(() => {
|
||||
it('should support date input, emit change', () => {
|
||||
jest.useFakeTimers()
|
||||
let result: string
|
||||
component.createdDateFromChange.subscribe((date) => (result = date))
|
||||
const input: HTMLInputElement = fixture.nativeElement.querySelector('input')
|
||||
input.value = '5/30/2023'
|
||||
input.dispatchEvent(new Event('change'))
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(result).not.toBeNull()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support date select, emit datesSet change', fakeAsync(() => {
|
||||
it('should support date select, emit datesSet change', () => {
|
||||
jest.useFakeTimers()
|
||||
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)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(result).not.toBeNull()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support relative dates', fakeAsync(() => {
|
||||
it('should support relative dates', () => {
|
||||
jest.useFakeTimers()
|
||||
let result: DateSelection
|
||||
component.datesSet.subscribe((date) => (result = date))
|
||||
component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown
|
||||
@@ -88,7 +88,7 @@ describe('DatesDropdownComponent', () => {
|
||||
} as any)
|
||||
component.addedRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown
|
||||
component.onSetAddedRelativeDate({ id: RelativeDate.WITHIN_1_WEEK } as any)
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(result).toEqual({
|
||||
createdFrom: null,
|
||||
createdTo: null,
|
||||
@@ -97,7 +97,8 @@ describe('DatesDropdownComponent', () => {
|
||||
addedTo: null,
|
||||
addedRelativeDateID: RelativeDate.WITHIN_1_WEEK,
|
||||
})
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support report if active', () => {
|
||||
component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK
|
||||
@@ -177,11 +178,12 @@ describe('DatesDropdownComponent', () => {
|
||||
expect(eventSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support debounce', fakeAsync(() => {
|
||||
it('should support debounce', () => {
|
||||
jest.useFakeTimers()
|
||||
let result: DateSelection
|
||||
component.datesSet.subscribe((date) => (result = date))
|
||||
component.onChangeDebounce()
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(result).toEqual({
|
||||
createdFrom: null,
|
||||
createdTo: null,
|
||||
@@ -190,5 +192,6 @@ describe('DatesDropdownComponent', () => {
|
||||
addedTo: null,
|
||||
addedRelativeDateID: null,
|
||||
})
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,12 +4,7 @@ import {
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import { Component } from '@angular/core'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import {
|
||||
FormControl,
|
||||
FormGroup,
|
||||
@@ -145,12 +140,14 @@ describe('EditDialogComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should delay close enabled', fakeAsync(() => {
|
||||
it('should delay close enabled', () => {
|
||||
jest.useFakeTimers()
|
||||
expect(component.closeEnabled).toBeFalsy()
|
||||
component.ngOnInit()
|
||||
tick(100)
|
||||
jest.advanceTimersByTime(100)
|
||||
expect(component.closeEnabled).toBeTruthy()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should set default owner when in create mode if unset', () => {
|
||||
component.dialogMode.set(EditDialogMode.CREATE)
|
||||
|
||||
+7
-10
@@ -3,12 +3,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgSelectModule } from '@ng-select/ng-select'
|
||||
@@ -76,7 +71,8 @@ describe('MailAccountEditDialogComponent', () => {
|
||||
expect(editTitleSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support test mail account and show appropriate expiring alert', fakeAsync(() => {
|
||||
it('should support test mail account and show appropriate expiring alert', () => {
|
||||
jest.useFakeTimers()
|
||||
component.object = {
|
||||
name: 'example',
|
||||
imap_server: 'imap.example.com',
|
||||
@@ -97,7 +93,7 @@ describe('MailAccountEditDialogComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Successfully connected'
|
||||
)
|
||||
tick(6000)
|
||||
jest.advanceTimersByTime(6000)
|
||||
fixture.detectChanges()
|
||||
expect(fixture.nativeElement.textContent).not.toContain(
|
||||
'Successfully connected'
|
||||
@@ -118,6 +114,7 @@ describe('MailAccountEditDialogComponent', () => {
|
||||
.flush({}, { status: 500, statusText: 'error' })
|
||||
fixture.detectChanges()
|
||||
expect(fixture.nativeElement.textContent).toContain('Unable to connect')
|
||||
tick(6000)
|
||||
}))
|
||||
jest.advanceTimersByTime(6000)
|
||||
jest.useRealTimers()
|
||||
})
|
||||
})
|
||||
|
||||
+69
-39
@@ -1,12 +1,7 @@
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { NEGATIVE_NULL_FILTER_VALUE } from 'src/app/data/filter-rule-type'
|
||||
import {
|
||||
@@ -52,6 +47,7 @@ const negativeNullItem = {
|
||||
}
|
||||
|
||||
let selectionModel: FilterableDropdownSelectionModel
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
|
||||
describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => {
|
||||
let component: FilterableDropdownComponent
|
||||
@@ -255,14 +251,17 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
expect(applyResult).toEqual({ itemsToAdd: [items[0]], itemsToRemove: [] })
|
||||
})
|
||||
|
||||
it('should focus text filter on open, support filtering, clear on close', fakeAsync(() => {
|
||||
it('should focus text filter on open, support filtering, clear on close', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
expect(document.activeElement).toEqual(
|
||||
component.listFilterTextInput.nativeElement
|
||||
)
|
||||
@@ -270,12 +269,16 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
|
||||
component.filterText = 'Tag2'
|
||||
fixture.detectChanges()
|
||||
expect(component.buttonsViewport.getRenderedRange().end).toEqual(1) // filtered
|
||||
component.buttonsViewport.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
expect(component.scrollViewportHeight).toEqual(
|
||||
component.FILTERABLE_BUTTON_HEIGHT_PX
|
||||
) // filtered
|
||||
component.dropdown.close()
|
||||
expect(component.filterText).toHaveLength(0)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should toggle & close on enter inside filter field if 1 item remains', fakeAsync(() => {
|
||||
it('should toggle & close on enter inside filter field if 1 item remains', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
expect(component.selectionModel.getSelectedItems()).toEqual([])
|
||||
@@ -283,7 +286,10 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.filterText = 'Tag2'
|
||||
fixture.detectChanges()
|
||||
const closeSpy = jest.spyOn(component.dropdown, 'close')
|
||||
@@ -291,11 +297,11 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
new KeyboardEvent('keyup', { key: 'Enter' })
|
||||
)
|
||||
expect(component.selectionModel.getSelectedItems()).toEqual([items[1]])
|
||||
tick(300)
|
||||
await wait(300)
|
||||
expect(closeSpy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should apply & close on enter inside filter field if 1 item remains if editing', fakeAsync(() => {
|
||||
it('should apply & close on enter inside filter field if 1 item remains if editing', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
component.editing = true
|
||||
@@ -306,25 +312,31 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.filterText = 'Tag2'
|
||||
fixture.detectChanges()
|
||||
component.listFilterTextInput.nativeElement.dispatchEvent(
|
||||
new KeyboardEvent('keyup', { key: 'Enter' })
|
||||
)
|
||||
expect(component.selectionModel.getSelectedItems()).toEqual([items[1]])
|
||||
tick(300)
|
||||
await wait(300)
|
||||
expect(applyResult).toEqual({ itemsToAdd: [items[1]], itemsToRemove: [] })
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support arrow keyboard navigation', fakeAsync(() => {
|
||||
it('should support arrow keyboard navigation', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
const filterInputEl: HTMLInputElement =
|
||||
@@ -362,16 +374,19 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
||||
)
|
||||
expect(document.activeElement).toEqual(itemButtons[0])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support arrow keyboard navigation after tab keyboard navigation', fakeAsync(() => {
|
||||
it('should support arrow keyboard navigation after tab keyboard navigation', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
const filterInputEl: HTMLInputElement =
|
||||
@@ -400,16 +415,19 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
||||
)
|
||||
expect(document.activeElement).toEqual(itemButtons[1])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support arrow keyboard navigation after click', fakeAsync(() => {
|
||||
it('should support arrow keyboard navigation after click', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
const filterInputEl: HTMLInputElement =
|
||||
@@ -427,9 +445,9 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
||||
)
|
||||
expect(document.activeElement).toEqual(itemButtons[1])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should toggle logical operator', fakeAsync(() => {
|
||||
it('should toggle logical operator', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
component.selectionModel.manyToOne = true
|
||||
@@ -445,7 +463,10 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(component.modifierToggleEnabled).toBeTruthy()
|
||||
const operatorButtons: HTMLInputElement[] = Array.from(
|
||||
@@ -456,9 +477,9 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
fixture.detectChanges()
|
||||
expect(selectionModel.logicalOperator).toEqual(LogicalOperator.Or)
|
||||
expect(changedResult.logicalOperator).toEqual(LogicalOperator.Or)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should toggle intersection include / exclude', fakeAsync(() => {
|
||||
it('should toggle intersection include / exclude', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
selectionModel.set(items[0].id, ToggleableItemState.Selected)
|
||||
@@ -473,7 +494,10 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(component.modifierToggleEnabled).toBeTruthy()
|
||||
const intersectionButtons: HTMLInputElement[] = Array.from(
|
||||
@@ -486,7 +510,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
expect(changedResult.intersection).toEqual(Intersection.Exclude)
|
||||
expect(changedResult.getSelectedItems()).toEqual([])
|
||||
expect(changedResult.getExcludedItems()).toEqual(items)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should update null item selection on toggleIntersection', () => {
|
||||
component.selectionModel.items = items
|
||||
@@ -819,7 +843,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
expect(getRootDocCount(rootWithoutCounts.id)).toEqual(0)
|
||||
})
|
||||
|
||||
it('should set support create, keep open model and call createRef method', fakeAsync(() => {
|
||||
it('should set support create, keep open model and call createRef method', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
component.selectionModel = selectionModel
|
||||
@@ -827,7 +851,10 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
fixture.detectChanges()
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
|
||||
component.filterText = 'Test Filter Text'
|
||||
component.createRef = jest.fn()
|
||||
@@ -837,9 +864,9 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
const openSpy = jest.spyOn(component.dropdown, 'open')
|
||||
component.dropdownOpenChange(false)
|
||||
expect(openSpy).toHaveBeenCalled() // should keep open
|
||||
}))
|
||||
})
|
||||
|
||||
it('should call create on enter inside filter field if 0 items remain while editing', fakeAsync(() => {
|
||||
it('should call create on enter inside filter field if 0 items remain while editing', async () => {
|
||||
component.selectionModel.items = items
|
||||
component.icon = 'tag-fill'
|
||||
component.editing = true
|
||||
@@ -849,12 +876,15 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
fixture.nativeElement
|
||||
.querySelector('button')
|
||||
.dispatchEvent(new MouseEvent('click')) // open
|
||||
tick(100)
|
||||
await wait(100)
|
||||
fixture.detectChanges()
|
||||
component.buttonsViewport?.checkViewportSize()
|
||||
fixture.detectChanges()
|
||||
component.filterText = 'FooBar'
|
||||
component.listFilterEnter()
|
||||
expect(component.selectionModel.getSelectedItems()).toEqual([])
|
||||
expect(createSpy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should exclude item and trigger change event', () => {
|
||||
const id = 1
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import {
|
||||
FormsModule,
|
||||
NG_VALUE_ACCESSOR,
|
||||
@@ -121,12 +116,14 @@ describe('SelectComponent', () => {
|
||||
).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should clear search term on blur after delay', fakeAsync(() => {
|
||||
it('should clear search term on blur after delay', () => {
|
||||
jest.useFakeTimers()
|
||||
const clearSpy = jest.spyOn(component, 'clearLastSearchTerm')
|
||||
component.onBlur()
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
expect(clearSpy).toHaveBeenCalled()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should emit filtered documents', () => {
|
||||
component.value = 10
|
||||
|
||||
@@ -143,13 +143,6 @@ describe('TagsComponent', () => {
|
||||
component.createTag()
|
||||
expect(modalService.hasOpenModals()).toBeTruthy()
|
||||
expect(activeInstances[0].componentInstance.object.name).toEqual('foobar')
|
||||
const editDialog = activeInstances[0]
|
||||
.componentInstance as TagEditDialogComponent
|
||||
editDialog.save() // create is mocked
|
||||
fixture.detectChanges()
|
||||
fixture.whenStable().then(() => {
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('foobar')
|
||||
})
|
||||
})
|
||||
|
||||
it('support remove tags', () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { Title } from '@angular/platform-browser'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { environment } from 'src/environments/environment'
|
||||
import { PageHeaderComponent } from './page-header.component'
|
||||
|
||||
@@ -13,7 +14,7 @@ describe('PageHeaderComponent', () => {
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [],
|
||||
imports: [PageHeaderComponent],
|
||||
imports: [NgxBootstrapIconsModule.pick(allIcons), PageHeaderComponent],
|
||||
}).compileComponents()
|
||||
|
||||
titleService = TestBed.inject(Title)
|
||||
@@ -23,6 +24,10 @@ describe('PageHeaderComponent', () => {
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should display title + subtitle', () => {
|
||||
fixture.componentRef.setInput('title', 'Foo')
|
||||
fixture.componentRef.setInput('subTitle', 'Bar')
|
||||
|
||||
+16
-15
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
@@ -191,7 +186,8 @@ describe('ProfileEditDialogComponent', () => {
|
||||
expect(component.saveDisabled).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should logout on save if password changed', fakeAsync(() => {
|
||||
it('should logout on save if password changed', () => {
|
||||
jest.useFakeTimers()
|
||||
const getSpy = jest.spyOn(profileService, 'get')
|
||||
getSpy.mockReturnValue(of(profile))
|
||||
const getProvidersSpy = jest.spyOn(
|
||||
@@ -211,13 +207,15 @@ describe('ProfileEditDialogComponent', () => {
|
||||
.mockImplementation(() => {})
|
||||
component.save()
|
||||
expect(updateSpy).toHaveBeenCalled()
|
||||
tick(2600)
|
||||
jest.advanceTimersByTime(2600)
|
||||
expect(navSpy).toHaveBeenCalledWith(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support auth token copy', fakeAsync(() => {
|
||||
it('should support auth token copy', () => {
|
||||
jest.useFakeTimers()
|
||||
const getSpy = jest.spyOn(profileService, 'get')
|
||||
getSpy.mockReturnValue(of(profile))
|
||||
const getProvidersSpy = jest.spyOn(
|
||||
@@ -230,9 +228,10 @@ describe('ProfileEditDialogComponent', () => {
|
||||
component.copyAuthToken()
|
||||
expect(copySpy).toHaveBeenCalledWith(profile.auth_token)
|
||||
expect(component.copied()).toBeTruthy()
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
expect(component.copied()).toBeFalsy()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support generate token, display error if needed', () => {
|
||||
const getSpy = jest.spyOn(profileService, 'get')
|
||||
@@ -366,11 +365,13 @@ describe('ProfileEditDialogComponent', () => {
|
||||
expect(component.isTotpEnabled).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should copy recovery codes', fakeAsync(() => {
|
||||
it('should copy recovery codes', () => {
|
||||
jest.useFakeTimers()
|
||||
const copySpy = jest.spyOn(clipboard, 'copy')
|
||||
component.recoveryCodes = ['1', '2', '3']
|
||||
component.copyRecoveryCodes()
|
||||
expect(copySpy).toHaveBeenCalledWith('1\n2\n3')
|
||||
tick(3000)
|
||||
}))
|
||||
jest.advanceTimersByTime(3000)
|
||||
jest.useRealTimers()
|
||||
})
|
||||
})
|
||||
|
||||
+6
-9
@@ -1,10 +1,5 @@
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { FileVersion } from 'src/app/data/share-link'
|
||||
@@ -108,7 +103,8 @@ describe('ShareLinkBundleDialogComponent', () => {
|
||||
expect(component.documentPreview()[0].id).toBe(1)
|
||||
})
|
||||
|
||||
it('copies share link and resets state after timeout', fakeAsync(() => {
|
||||
it('copies share link and resets state after timeout', () => {
|
||||
jest.useFakeTimers()
|
||||
const copySpy = jest.spyOn(clipboard, 'copy').mockReturnValue(true)
|
||||
const bundle = {
|
||||
slug: 'bundle-slug',
|
||||
@@ -121,9 +117,10 @@ describe('ShareLinkBundleDialogComponent', () => {
|
||||
expect(component.copied()).toBe(true)
|
||||
expect(toastService.showInfo).toHaveBeenCalled()
|
||||
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
expect(component.copied()).toBe(false)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('generates share URLs based on API base URL', () => {
|
||||
environment.apiBaseUrl = 'https://example.com/api/'
|
||||
|
||||
+28
-44
@@ -1,10 +1,5 @@
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of, throwError } from 'rxjs'
|
||||
@@ -70,6 +65,7 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
fixture.destroy()
|
||||
environment.apiBaseUrl = originalApiBaseUrl
|
||||
jest.clearAllMocks()
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
const sampleBundle = (overrides: Partial<ShareLinkBundleSummary> = {}) =>
|
||||
@@ -85,7 +81,8 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
...overrides,
|
||||
}) as ShareLinkBundleSummary
|
||||
|
||||
it('loads bundles on init and polls periodically', fakeAsync(() => {
|
||||
it('loads bundles on init and polls periodically', () => {
|
||||
jest.useFakeTimers()
|
||||
const bundles = [sampleBundle({ status: ShareLinkBundleStatus.Ready })]
|
||||
service.listAllBundles.mockReset()
|
||||
service.listAllBundles
|
||||
@@ -93,38 +90,37 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
.mockReturnValue(of(bundles))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
expect(service.listAllBundles).toHaveBeenCalledTimes(1)
|
||||
expect(component.bundles()).toEqual(bundles)
|
||||
expect(component.loading()).toBe(false)
|
||||
expect(component.error()).toBeNull()
|
||||
|
||||
tick(5000)
|
||||
jest.advanceTimersByTime(5000)
|
||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
||||
}))
|
||||
})
|
||||
|
||||
it('handles errors when loading bundles', fakeAsync(() => {
|
||||
it('handles errors when loading bundles', () => {
|
||||
jest.useFakeTimers()
|
||||
service.listAllBundles.mockReset()
|
||||
service.listAllBundles
|
||||
.mockReturnValueOnce(throwError(() => new Error('load fail')))
|
||||
.mockReturnValue(of([]))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
expect(component.error()).toContain('Failed to load share link bundles.')
|
||||
expect(toastService.showError).toHaveBeenCalled()
|
||||
expect(component.loading()).toBe(false)
|
||||
|
||||
tick(5000)
|
||||
jest.advanceTimersByTime(5000)
|
||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
||||
}))
|
||||
})
|
||||
|
||||
it('copies bundle links when ready', fakeAsync(() => {
|
||||
it('copies bundle links when ready', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(clipboard, 'copy').mockReturnValue(true)
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
const readyBundle = sampleBundle({
|
||||
slug: 'ready-slug',
|
||||
@@ -138,27 +134,24 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
expect(component.copiedSlug()).toBe('ready-slug')
|
||||
expect(toastService.showInfo).toHaveBeenCalled()
|
||||
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
expect(component.copiedSlug()).toBeNull()
|
||||
}))
|
||||
})
|
||||
|
||||
it('ignores copy requests for non-ready bundles', fakeAsync(() => {
|
||||
it('ignores copy requests for non-ready bundles', () => {
|
||||
const copySpy = jest.spyOn(clipboard, 'copy')
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
component.copy(sampleBundle({ status: ShareLinkBundleStatus.Pending }))
|
||||
expect(copySpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('deletes bundles and refreshes list', fakeAsync(() => {
|
||||
it('deletes bundles and refreshes list', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
service.delete.mockReturnValue(of(true))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
component.delete(sampleBundle())
|
||||
tick()
|
||||
|
||||
expect(service.delete).toHaveBeenCalled()
|
||||
expect(toastService.showInfo).toHaveBeenCalledWith(
|
||||
@@ -166,72 +159,63 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
)
|
||||
expect(service.listAllBundles).toHaveBeenCalledTimes(2)
|
||||
expect(component.loading()).toBe(false)
|
||||
}))
|
||||
})
|
||||
|
||||
it('handles delete errors gracefully', fakeAsync(() => {
|
||||
it('handles delete errors gracefully', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
service.delete.mockReturnValue(throwError(() => new Error('delete fail')))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
component.delete(sampleBundle())
|
||||
tick()
|
||||
|
||||
expect(toastService.showError).toHaveBeenCalled()
|
||||
expect(component.loading()).toBe(false)
|
||||
}))
|
||||
})
|
||||
|
||||
it('retries bundle build and replaces existing entry', fakeAsync(() => {
|
||||
it('retries bundle build and replaces existing entry', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
const updated = sampleBundle({ status: ShareLinkBundleStatus.Ready })
|
||||
service.rebuildBundle.mockReturnValue(of(updated))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
component.bundles.set([sampleBundle()])
|
||||
component.retry(component.bundles()[0])
|
||||
tick()
|
||||
|
||||
expect(service.rebuildBundle).toHaveBeenCalledWith(updated.id)
|
||||
expect(component.bundles()[0].status).toBe(ShareLinkBundleStatus.Ready)
|
||||
expect(toastService.showInfo).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('adds new bundle when retry returns unknown entry', fakeAsync(() => {
|
||||
it('adds new bundle when retry returns unknown entry', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
service.rebuildBundle.mockReturnValue(
|
||||
of(sampleBundle({ id: 99, slug: 'new-slug' }))
|
||||
)
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
component.bundles.set([sampleBundle()])
|
||||
component.retry({ id: 99 } as ShareLinkBundleSummary)
|
||||
tick()
|
||||
|
||||
expect(component.bundles().find((bundle) => bundle.id === 99)).toBeTruthy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('handles retry errors', fakeAsync(() => {
|
||||
it('handles retry errors', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
service.rebuildBundle.mockReturnValue(throwError(() => new Error('fail')))
|
||||
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
component.retry(sampleBundle())
|
||||
tick()
|
||||
|
||||
expect(toastService.showError).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('maps helpers and closes dialog', fakeAsync(() => {
|
||||
it('maps helpers and closes dialog', () => {
|
||||
service.listAllBundles.mockReturnValue(of([]))
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
|
||||
expect(component.statusLabel(ShareLinkBundleStatus.Processing)).toContain(
|
||||
'Processing'
|
||||
@@ -247,5 +231,5 @@ describe('ShareLinkBundleManageDialogComponent', () => {
|
||||
const closeSpy = jest.spyOn(activeModal, 'close')
|
||||
component.close()
|
||||
expect(closeSpy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
+7
-10
@@ -4,12 +4,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -103,7 +98,8 @@ describe('ShareLinksDialogComponent', () => {
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should support link creation then refresh & copy url', fakeAsync(() => {
|
||||
it('should support link creation then refresh & copy url', () => {
|
||||
jest.useFakeTimers()
|
||||
const createSpy = jest.spyOn(shareLinkService, 'createLinkForDocument')
|
||||
component.documentId.set(99)
|
||||
component.expirationDays = 7
|
||||
@@ -126,13 +122,14 @@ describe('ShareLinksDialogComponent', () => {
|
||||
expiration: expiration.toISOString(),
|
||||
})
|
||||
fixture.detectChanges()
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
|
||||
expect(refreshSpy).toHaveBeenCalled()
|
||||
expect(copySpy).toHaveBeenCalled()
|
||||
expect(component.copied()).toEqual(1)
|
||||
tick(100) // copy timeout
|
||||
}))
|
||||
jest.advanceTimersByTime(100) // copy timeout
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should show error on link creation if needed', () => {
|
||||
component.documentId.set(99)
|
||||
|
||||
+6
-9
@@ -16,12 +16,7 @@ jest.mock('src/environments/environment', () => ({
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { Subject, of, throwError } from 'rxjs'
|
||||
@@ -126,16 +121,18 @@ describe('SystemStatusDialogComponent', () => {
|
||||
expect(closeSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should copy the system status to clipboard', fakeAsync(() => {
|
||||
it('should copy the system status to clipboard', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(clipboard, 'copy')
|
||||
component.copy()
|
||||
expect(clipboard.copy).toHaveBeenCalledWith(
|
||||
JSON.stringify(component.status(), null, 4)
|
||||
)
|
||||
expect(component.copied()).toBeTruthy()
|
||||
tick(3000)
|
||||
jest.advanceTimersByTime(3000)
|
||||
expect(component.copied()).toBeFalsy()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should calculate if date is stale', () => {
|
||||
const date = new Date()
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
flush,
|
||||
TestBed,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
|
||||
import { Clipboard } from '@angular/cdk/clipboard'
|
||||
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
@@ -48,28 +41,25 @@ describe('ToastComponent', () => {
|
||||
expect(component).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should countdown toast', fakeAsync(() => {
|
||||
it('should countdown toast', () => {
|
||||
jest.useFakeTimers()
|
||||
component.toast = toast2
|
||||
fixture.detectChanges()
|
||||
component.onShown(toast2)
|
||||
tick(5000)
|
||||
jest.advanceTimersByTime(5000)
|
||||
expect(component.toast.delayRemaining).toEqual(0)
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should show an error if given with toast', fakeAsync(() => {
|
||||
it('should show an error if given with toast', () => {
|
||||
component.toast = toast1
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(fixture.nativeElement.querySelector('details')).not.toBeNull()
|
||||
expect(fixture.nativeElement.textContent).toContain('Error 1 content')
|
||||
})
|
||||
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
|
||||
it('should show error details, support copy', fakeAsync(() => {
|
||||
it('should show error details, support copy', () => {
|
||||
component.toast = toast2
|
||||
fixture.detectChanges()
|
||||
|
||||
@@ -81,10 +71,7 @@ describe('ToastComponent', () => {
|
||||
const copySpy = jest.spyOn(clipboard, 'copy')
|
||||
component.copyError(toast2.error)
|
||||
expect(copySpy).toHaveBeenCalled()
|
||||
|
||||
flush()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should parse error text, add ellipsis', () => {
|
||||
expect(component.getErrorText(toast2.error)).toEqual(
|
||||
|
||||
+7
-9
@@ -2,12 +2,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { DatePipe } from '@angular/common'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { Router } from '@angular/router'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
@@ -186,7 +181,8 @@ describe('SavedViewWidgetComponent', () => {
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should show a list of documents', fakeAsync(() => {
|
||||
it('should show a list of documents', async () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(documentService, 'listFiltered').mockReturnValue(
|
||||
of({
|
||||
all: [2, 3],
|
||||
@@ -195,7 +191,8 @@ describe('SavedViewWidgetComponent', () => {
|
||||
})
|
||||
)
|
||||
component.ngOnInit()
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
await fixture.whenStable()
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('doc2')
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('doc3')
|
||||
@@ -206,7 +203,8 @@ describe('SavedViewWidgetComponent', () => {
|
||||
expect(
|
||||
fixture.debugElement.queryAll(By.css('td a.btn'))[1].attributes['href']
|
||||
).toEqual(component.getDownloadUrl(documentResults[0]))
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should call api endpoint and load results', () => {
|
||||
const listAllSpy = jest.spyOn(documentService, 'listFiltered')
|
||||
|
||||
+6
-9
@@ -1,11 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
@@ -133,7 +128,8 @@ describe('UploadFileWidgetComponent', () => {
|
||||
expect(dismissSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should allow dismissing completed alerts', fakeAsync(() => {
|
||||
it('should allow dismissing completed alerts', () => {
|
||||
jest.useFakeTimers()
|
||||
mockConsumerStatuses(websocketStatusService)
|
||||
fixture.detectChanges()
|
||||
jest
|
||||
@@ -141,10 +137,11 @@ describe('UploadFileWidgetComponent', () => {
|
||||
.mockImplementation(() => SUCCESS_STATUSES)
|
||||
const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss')
|
||||
component.dismissCompleted()
|
||||
tick(1000)
|
||||
jest.advanceTimersByTime(1000)
|
||||
fixture.detectChanges()
|
||||
expect(dismissSpy).toHaveBeenCalledTimes(4)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
})
|
||||
|
||||
function mockConsumerStatuses(consumerStatusService) {
|
||||
|
||||
@@ -9,13 +9,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import {
|
||||
ActivatedRoute,
|
||||
@@ -279,6 +273,7 @@ describe('DocumentDetailComponent', () => {
|
||||
}).compileComponents()
|
||||
|
||||
router = TestBed.inject(Router)
|
||||
jest.spyOn(router, 'navigate').mockResolvedValue(true)
|
||||
activatedRoute = TestBed.inject(ActivatedRoute)
|
||||
openDocumentsService = TestBed.inject(OpenDocumentsService)
|
||||
documentService = TestBed.inject(DocumentService)
|
||||
@@ -295,6 +290,10 @@ describe('DocumentDetailComponent', () => {
|
||||
component = fixture.componentInstance
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
function initNormally() {
|
||||
jest
|
||||
.spyOn(activatedRoute, 'paramMap', 'get')
|
||||
@@ -330,7 +329,8 @@ describe('DocumentDetailComponent', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should switch from preview to details when pdf preview enters the DOM', fakeAsync(() => {
|
||||
it('should switch from preview to details when pdf preview enters the DOM', () => {
|
||||
jest.useFakeTimers()
|
||||
component.nav = {
|
||||
activeId: component.DocumentDetailNavIDs.Preview,
|
||||
select: jest.fn(),
|
||||
@@ -339,11 +339,11 @@ describe('DocumentDetailComponent', () => {
|
||||
nativeElement: { offsetParent: {} },
|
||||
}
|
||||
|
||||
tick()
|
||||
jest.advanceTimersByTime(0)
|
||||
expect(component.nav.select).toHaveBeenCalledWith(
|
||||
component.DocumentDetailNavIDs.Details
|
||||
)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should forward title key up value to titleSubject', () => {
|
||||
const subjectSpy = jest.spyOn(component.titleSubject, 'next')
|
||||
@@ -377,24 +377,25 @@ describe('DocumentDetailComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should update title after debounce', fakeAsync(() => {
|
||||
it('should update title after debounce', () => {
|
||||
jest.useFakeTimers()
|
||||
initNormally()
|
||||
component.titleInput.value = 'Foo Bar'
|
||||
component.titleSubject.next('Foo Bar')
|
||||
tick(1000)
|
||||
jest.advanceTimersByTime(1000)
|
||||
expect(component.documentForm.get('title').value).toEqual('Foo Bar')
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should update title before doc change if was not updated via debounce', fakeAsync(() => {
|
||||
it('should update title before doc change if was not updated via debounce', () => {
|
||||
jest.useFakeTimers()
|
||||
initNormally()
|
||||
component.titleInput.value = 'Foo Bar'
|
||||
component.titleInput.inputField.nativeElement.dispatchEvent(
|
||||
new Event('change')
|
||||
)
|
||||
tick(1000)
|
||||
jest.advanceTimersByTime(1000)
|
||||
expect(component.documentForm.get('title').value).toEqual('Foo Bar')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should load non-open document via param', () => {
|
||||
initNormally()
|
||||
@@ -1035,14 +1036,13 @@ describe('DocumentDetailComponent', () => {
|
||||
expect(component.previewNumPages()).toEqual(1000)
|
||||
})
|
||||
|
||||
it('should include delay of 300ms after previewloaded before showing pdf', fakeAsync(() => {
|
||||
it('should mark preview loaded after pdf loads', () => {
|
||||
initNormally()
|
||||
expect(component.previewLoaded()).toBeFalsy()
|
||||
component.pdfPreviewLoaded({ numPages: 1000 } as any)
|
||||
expect(component.previewNumPages()).toEqual(1000)
|
||||
tick(300)
|
||||
expect(component.previewLoaded()).toBeTruthy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support zoom controls', () => {
|
||||
initNormally()
|
||||
@@ -2183,7 +2183,8 @@ describe('DocumentDetailComponent', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should print document successfully', fakeAsync(() => {
|
||||
it('should print document successfully', () => {
|
||||
jest.useFakeTimers()
|
||||
initNormally()
|
||||
|
||||
const appendChildSpy = jest
|
||||
@@ -2224,8 +2225,6 @@ describe('DocumentDetailComponent', () => {
|
||||
)
|
||||
req.flush(blob)
|
||||
|
||||
tick()
|
||||
|
||||
expect(createElementSpy).toHaveBeenCalledWith('iframe')
|
||||
expect(appendChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||
expect(createObjectURLSpy).toHaveBeenCalledWith(blob)
|
||||
@@ -2242,7 +2241,7 @@ describe('DocumentDetailComponent', () => {
|
||||
if (mockIframe.onload) {
|
||||
mockIframe.onload({} as any)
|
||||
}
|
||||
tick()
|
||||
jest.advanceTimersByTime(0)
|
||||
|
||||
expect(mockContentWindow.focus).toHaveBeenCalled()
|
||||
expect(mockContentWindow.print).toHaveBeenCalled()
|
||||
@@ -2255,8 +2254,6 @@ describe('DocumentDetailComponent', () => {
|
||||
mockContentWindow.onafterprint(new Event('afterprint'))
|
||||
}
|
||||
|
||||
tick(500)
|
||||
|
||||
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
||||
|
||||
@@ -2265,7 +2262,7 @@ describe('DocumentDetailComponent', () => {
|
||||
removeChildSpy.mockRestore()
|
||||
createObjectURLSpy.mockRestore()
|
||||
revokeObjectURLSpy.mockRestore()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should show error toast if print document fails', () => {
|
||||
initNormally()
|
||||
@@ -2302,76 +2299,71 @@ describe('DocumentDetailComponent', () => {
|
||||
]
|
||||
|
||||
iframePrintErrorCases.forEach(({ description, thrownError, expectToast }) => {
|
||||
it(
|
||||
description,
|
||||
fakeAsync(() => {
|
||||
initNormally()
|
||||
it(description, () => {
|
||||
jest.useFakeTimers()
|
||||
initNormally()
|
||||
|
||||
const appendChildSpy = jest
|
||||
.spyOn(document.body, 'appendChild')
|
||||
.mockImplementation((node: Node) => node)
|
||||
const removeChildSpy = jest
|
||||
.spyOn(document.body, 'removeChild')
|
||||
.mockImplementation((node: Node) => node)
|
||||
const createObjectURLSpy = jest
|
||||
.spyOn(URL, 'createObjectURL')
|
||||
.mockReturnValue('blob:mock-url')
|
||||
const revokeObjectURLSpy = jest
|
||||
.spyOn(URL, 'revokeObjectURL')
|
||||
.mockImplementation(() => {})
|
||||
const appendChildSpy = jest
|
||||
.spyOn(document.body, 'appendChild')
|
||||
.mockImplementation((node: Node) => node)
|
||||
const removeChildSpy = jest
|
||||
.spyOn(document.body, 'removeChild')
|
||||
.mockImplementation((node: Node) => node)
|
||||
const createObjectURLSpy = jest
|
||||
.spyOn(URL, 'createObjectURL')
|
||||
.mockReturnValue('blob:mock-url')
|
||||
const revokeObjectURLSpy = jest
|
||||
.spyOn(URL, 'revokeObjectURL')
|
||||
.mockImplementation(() => {})
|
||||
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
|
||||
const mockContentWindow = {
|
||||
focus: jest.fn().mockImplementation(() => {
|
||||
throw thrownError
|
||||
}),
|
||||
print: jest.fn(),
|
||||
onafterprint: null,
|
||||
}
|
||||
const mockContentWindow = {
|
||||
focus: jest.fn().mockImplementation(() => {
|
||||
throw thrownError
|
||||
}),
|
||||
print: jest.fn(),
|
||||
onafterprint: null,
|
||||
}
|
||||
|
||||
const mockIframe: any = {
|
||||
style: {},
|
||||
src: '',
|
||||
onload: null,
|
||||
contentWindow: mockContentWindow,
|
||||
}
|
||||
const mockIframe: any = {
|
||||
style: {},
|
||||
src: '',
|
||||
onload: null,
|
||||
contentWindow: mockContentWindow,
|
||||
}
|
||||
|
||||
const createElementSpy = jest
|
||||
.spyOn(document, 'createElement')
|
||||
.mockReturnValue(mockIframe as any)
|
||||
const createElementSpy = jest
|
||||
.spyOn(document, 'createElement')
|
||||
.mockReturnValue(mockIframe as any)
|
||||
|
||||
const blob = new Blob(['test'], { type: 'application/pdf' })
|
||||
component.printDocument()
|
||||
const blob = new Blob(['test'], { type: 'application/pdf' })
|
||||
component.printDocument()
|
||||
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/${doc.id}/download/`
|
||||
)
|
||||
req.flush(blob)
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}documents/${doc.id}/download/`
|
||||
)
|
||||
req.flush(blob)
|
||||
|
||||
tick()
|
||||
if (mockIframe.onload) {
|
||||
mockIframe.onload(new Event('load'))
|
||||
}
|
||||
|
||||
if (mockIframe.onload) {
|
||||
mockIframe.onload(new Event('load'))
|
||||
}
|
||||
jest.advanceTimersByTime(200)
|
||||
|
||||
tick()
|
||||
tick(200)
|
||||
if (expectToast) {
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
} else {
|
||||
expect(toastSpy).not.toHaveBeenCalled()
|
||||
}
|
||||
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
||||
|
||||
if (expectToast) {
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
} else {
|
||||
expect(toastSpy).not.toHaveBeenCalled()
|
||||
}
|
||||
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
||||
|
||||
createElementSpy.mockRestore()
|
||||
appendChildSpy.mockRestore()
|
||||
removeChildSpy.mockRestore()
|
||||
createObjectURLSpy.mockRestore()
|
||||
revokeObjectURLSpy.mockRestore()
|
||||
})
|
||||
)
|
||||
createElementSpy.mockRestore()
|
||||
appendChildSpy.mockRestore()
|
||||
removeChildSpy.mockRestore()
|
||||
createObjectURLSpy.mockRestore()
|
||||
revokeObjectURLSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+183
-178
@@ -4,12 +4,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { RouterModule } from '@angular/router'
|
||||
@@ -189,8 +184,13 @@ describe('FilterEditorComponent', () => {
|
||||
let httpTestingController: HttpTestingController
|
||||
let searchService: SearchService
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
const tick = (ms: number = 0) => {
|
||||
jest.advanceTimersByTime(ms)
|
||||
fixture.detectChanges()
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterModule,
|
||||
NgbDropdownModule,
|
||||
@@ -275,8 +275,13 @@ describe('FilterEditorComponent', () => {
|
||||
component = fixture.componentInstance
|
||||
component.filterRules = []
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
}))
|
||||
await fixture.whenStable()
|
||||
jest.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should not attempt to retrieve objects if user does not have permissions', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReset()
|
||||
@@ -298,7 +303,7 @@ describe('FilterEditorComponent', () => {
|
||||
|
||||
// SET filterRules
|
||||
|
||||
it('should ingest text filter rules for doc title', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc title', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -308,9 +313,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('foo')
|
||||
expect(component.textFilterTarget).toEqual('title') // TEXT_FILTER_TARGET_TITLE
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc title + content', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc title + content', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -320,9 +325,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('foo')
|
||||
expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest legacy text filter rules for doc title + content', fakeAsync(() => {
|
||||
it('should ingest legacy text filter rules for doc title + content', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -332,9 +337,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('legacy foo')
|
||||
expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc asn', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc asn', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -344,9 +349,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('foo')
|
||||
expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for custom fields', fakeAsync(() => {
|
||||
it('should ingest text filter rules for custom fields', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -356,9 +361,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('foo')
|
||||
expect(component.textFilterTarget).toEqual('custom-fields') // TEXT_FILTER_TARGET_CUSTOM_FIELDS
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc asn is null', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc asn is null', () => {
|
||||
expect(component.textFilterTarget).toEqual('title-content')
|
||||
expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS
|
||||
component.filterRules = [
|
||||
@@ -369,9 +374,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN
|
||||
expect(component.textFilterModifier).toEqual('is null') // TEXT_FILTER_MODIFIER_NULL
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc asn is not null', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc asn is not null', () => {
|
||||
expect(component.textFilterTarget).toEqual('title-content')
|
||||
expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS
|
||||
component.filterRules = [
|
||||
@@ -382,9 +387,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN
|
||||
expect(component.textFilterModifier).toEqual('not null') // TEXT_FILTER_MODIFIER_NOTNULL
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc asn greater than', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc asn greater than', () => {
|
||||
expect(component.textFilterTarget).toEqual('title-content')
|
||||
expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS
|
||||
component.filterRules = [
|
||||
@@ -395,9 +400,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN
|
||||
expect(component.textFilterModifier).toEqual('greater') // TEXT_FILTER_MODIFIER_GT
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for doc asn less than', fakeAsync(() => {
|
||||
it('should ingest text filter rules for doc asn less than', () => {
|
||||
expect(component.textFilterTarget).toEqual('title-content')
|
||||
expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS
|
||||
component.filterRules = [
|
||||
@@ -408,9 +413,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN
|
||||
expect(component.textFilterModifier).toEqual('less') // TEXT_FILTER_MODIFIER_LT
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for mime type', fakeAsync(() => {
|
||||
it('should ingest text filter rules for mime type', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -420,9 +425,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('pdf')
|
||||
expect(component.textFilterTarget).toEqual('mime-type') // TEXT_FILTER_TARGET_MIME_TYPE
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for fulltext query', fakeAsync(() => {
|
||||
it('should ingest text filter rules for fulltext query', () => {
|
||||
expect(component.textFilter).toEqual(null)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -432,9 +437,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.textFilter).toEqual('foo,bar')
|
||||
expect(component.textFilterTarget).toEqual('fulltext-query') // TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for fulltext query that include date created', fakeAsync(() => {
|
||||
it('should ingest text filter rules for fulltext query that include date created', () => {
|
||||
expect(component.dateCreatedRelativeDate).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -444,9 +449,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.dateCreatedRelativeDate).toEqual(1) // RELATIVE_DATE_QUERYSTRINGS['-1 week to now']
|
||||
expect(component.textFilter).toBeNull()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for fulltext query that include date added', fakeAsync(() => {
|
||||
it('should ingest text filter rules for fulltext query that include date added', () => {
|
||||
expect(component.dateAddedRelativeDate).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -456,9 +461,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.dateAddedRelativeDate).toEqual(1) // RELATIVE_DATE_QUERYSTRINGS['-1 week to now']
|
||||
expect(component.textFilter).toBeNull()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter content with relative dates that are not in quick list', fakeAsync(() => {
|
||||
it('should ingest text filter content with relative dates that are not in quick list', () => {
|
||||
expect(component.dateAddedRelativeDate).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -478,9 +483,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.dateCreatedRelativeDate).toBeNull()
|
||||
expect(component.textFilter).toEqual('created:[-2 week to now]')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest text filter rules for more like', fakeAsync(() => {
|
||||
it('should ingest text filter rules for more like', () => {
|
||||
const moreLikeSpy = jest.spyOn(documentService, 'get')
|
||||
moreLikeSpy.mockReturnValue(of({ id: 1, title: 'Foo Bar' }))
|
||||
expect(component.textFilter).toEqual(null)
|
||||
@@ -500,9 +505,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date created after and adjust date by 1 day', fakeAsync(() => {
|
||||
it('should ingest filter rules for date created after and adjust date by 1 day', () => {
|
||||
expect(component.dateCreatedFrom).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -511,9 +516,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateCreatedFrom).toEqual('2023-05-15')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date created from', fakeAsync(() => {
|
||||
it('should ingest filter rules for date created from', () => {
|
||||
expect(component.dateCreatedFrom).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -522,9 +527,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateCreatedFrom).toEqual('2023-05-14')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date created before and adjust date by 1 day', fakeAsync(() => {
|
||||
it('should ingest filter rules for date created before and adjust date by 1 day', () => {
|
||||
expect(component.dateCreatedTo).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -533,9 +538,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateCreatedTo).toEqual('2023-05-13')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date created to', fakeAsync(() => {
|
||||
it('should ingest filter rules for date created to', () => {
|
||||
expect(component.dateCreatedTo).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -544,9 +549,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateCreatedTo).toEqual('2023-05-14')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date added after and adjust date by 1 day', fakeAsync(() => {
|
||||
it('should ingest filter rules for date added after and adjust date by 1 day', () => {
|
||||
expect(component.dateAddedFrom).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -555,9 +560,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateAddedFrom).toEqual('2023-05-15')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date added from', fakeAsync(() => {
|
||||
it('should ingest filter rules for date added from', () => {
|
||||
expect(component.dateAddedFrom).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -566,9 +571,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateAddedFrom).toEqual('2023-05-14')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date added before and adjust date by 1 day', fakeAsync(() => {
|
||||
it('should ingest filter rules for date added before and adjust date by 1 day', () => {
|
||||
expect(component.dateAddedTo).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -577,9 +582,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateAddedTo).toEqual('2023-05-13')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for date added to', fakeAsync(() => {
|
||||
it('should ingest filter rules for date added to', () => {
|
||||
expect(component.dateAddedTo).toBeNull()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -588,9 +593,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.dateAddedTo).toEqual('2023-05-14')
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has all tags', fakeAsync(() => {
|
||||
it('should ingest filter rules for has all tags', () => {
|
||||
expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -614,9 +619,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
component.toggleTag(2) // coverage
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any tags', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any tags', () => {
|
||||
expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -639,9 +644,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any tag', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any tag', () => {
|
||||
expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -651,9 +656,9 @@ describe('FilterEditorComponent', () => {
|
||||
]
|
||||
expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(1)
|
||||
expect(component.tagSelectionModel.get(null)).toBeTruthy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for exclude tag(s)', fakeAsync(() => {
|
||||
it('should ingest filter rules for exclude tag(s)', () => {
|
||||
expect(component.tagSelectionModel.getExcludedItems()).toHaveLength(0)
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -676,9 +681,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has correspondent', fakeAsync(() => {
|
||||
it('should ingest filter rules for has correspondent', () => {
|
||||
expect(
|
||||
component.correspondentSelectionModel.getSelectedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -708,9 +713,9 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.correspondentSelectionModel.getExcludedItems()).toEqual([
|
||||
{ id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' },
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any of correspondents', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any of correspondents', () => {
|
||||
expect(
|
||||
component.correspondentSelectionModel.getSelectedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -740,9 +745,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for does not have any of correspondents', fakeAsync(() => {
|
||||
it('should ingest filter rules for does not have any of correspondents', () => {
|
||||
expect(
|
||||
component.correspondentSelectionModel.getExcludedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -769,9 +774,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has document type', fakeAsync(() => {
|
||||
it('should ingest filter rules for has document type', () => {
|
||||
expect(
|
||||
component.documentTypeSelectionModel.getSelectedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -801,9 +806,9 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.documentTypeSelectionModel.getExcludedItems()).toEqual([
|
||||
{ id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' },
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any of document types', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any of document types', () => {
|
||||
expect(
|
||||
component.documentTypeSelectionModel.getSelectedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -830,9 +835,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for does not have any of document types', fakeAsync(() => {
|
||||
it('should ingest filter rules for does not have any of document types', () => {
|
||||
expect(
|
||||
component.documentTypeSelectionModel.getExcludedItems()
|
||||
).toHaveLength(0)
|
||||
@@ -859,9 +864,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has storage path', fakeAsync(() => {
|
||||
it('should ingest filter rules for has storage path', () => {
|
||||
expect(component.storagePathSelectionModel.getSelectedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
@@ -891,9 +896,9 @@ describe('FilterEditorComponent', () => {
|
||||
expect(component.storagePathSelectionModel.getExcludedItems()).toEqual([
|
||||
{ id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' },
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any of storage paths', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any of storage paths', () => {
|
||||
expect(component.storagePathSelectionModel.getSelectedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
@@ -923,9 +928,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for does not have any of storage paths', fakeAsync(() => {
|
||||
it('should ingest filter rules for does not have any of storage paths', () => {
|
||||
expect(component.storagePathSelectionModel.getExcludedItems()).toHaveLength(
|
||||
0
|
||||
)
|
||||
@@ -952,9 +957,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for custom fields all', fakeAsync(() => {
|
||||
it('should ingest filter rules for custom fields all', () => {
|
||||
expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -972,9 +977,9 @@ describe('FilterEditorComponent', () => {
|
||||
.value[0] as CustomFieldQueryAtom
|
||||
).serialize()
|
||||
).toEqual(['42', CustomFieldQueryOperator.Exists, 'true'])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for has any custom fields', fakeAsync(() => {
|
||||
it('should ingest filter rules for has any custom fields', () => {
|
||||
expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -992,9 +997,9 @@ describe('FilterEditorComponent', () => {
|
||||
.value[0] as CustomFieldQueryAtom
|
||||
).serialize()
|
||||
).toEqual(['42', CustomFieldQueryOperator.Exists, 'true'])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for custom field queries', fakeAsync(() => {
|
||||
it('should ingest filter rules for custom field queries', () => {
|
||||
expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy()
|
||||
component.filterRules = [
|
||||
{
|
||||
@@ -1027,9 +1032,9 @@ describe('FilterEditorComponent', () => {
|
||||
.value[0] as CustomFieldQueryAtom
|
||||
).serialize()
|
||||
).toEqual([42, CustomFieldQueryOperator.Exists, 'true'])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for owner', fakeAsync(() => {
|
||||
it('should ingest filter rules for owner', () => {
|
||||
expect(component.permissionsSelectionModel.ownerFilter).toEqual(
|
||||
OwnerFilterType.NONE
|
||||
)
|
||||
@@ -1044,9 +1049,9 @@ describe('FilterEditorComponent', () => {
|
||||
)
|
||||
expect(component.permissionsSelectionModel.hideUnowned).toBeFalsy()
|
||||
expect(component.permissionsSelectionModel.userID).toEqual(100)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for owner is others', fakeAsync(() => {
|
||||
it('should ingest filter rules for owner is others', () => {
|
||||
expect(component.permissionsSelectionModel.ownerFilter).toEqual(
|
||||
OwnerFilterType.NONE
|
||||
)
|
||||
@@ -1060,9 +1065,9 @@ describe('FilterEditorComponent', () => {
|
||||
OwnerFilterType.OTHERS
|
||||
)
|
||||
expect(component.permissionsSelectionModel.includeUsers).toContain(50)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for owner does not include others', fakeAsync(() => {
|
||||
it('should ingest filter rules for owner does not include others', () => {
|
||||
expect(component.permissionsSelectionModel.ownerFilter).toEqual(
|
||||
OwnerFilterType.NONE
|
||||
)
|
||||
@@ -1076,9 +1081,9 @@ describe('FilterEditorComponent', () => {
|
||||
OwnerFilterType.NOT_SELF
|
||||
)
|
||||
expect(component.permissionsSelectionModel.excludeUsers).toContain(50)
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for owner is null', fakeAsync(() => {
|
||||
it('should ingest filter rules for owner is null', () => {
|
||||
expect(component.permissionsSelectionModel.ownerFilter).toEqual(
|
||||
OwnerFilterType.NONE
|
||||
)
|
||||
@@ -1092,9 +1097,9 @@ describe('FilterEditorComponent', () => {
|
||||
OwnerFilterType.UNOWNED
|
||||
)
|
||||
expect(component.permissionsSelectionModel.hideUnowned).toBeFalsy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for owner is not null', fakeAsync(() => {
|
||||
it('should ingest filter rules for owner is not null', () => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_OWNER_ISNULL,
|
||||
@@ -1109,9 +1114,9 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.permissionsSelectionModel.hideUnowned).toBeTruthy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should ingest filter rules for shared by me', fakeAsync(() => {
|
||||
it('should ingest filter rules for shared by me', () => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_SHARED_BY_USER,
|
||||
@@ -1119,11 +1124,11 @@ describe('FilterEditorComponent', () => {
|
||||
},
|
||||
]
|
||||
expect(component.permissionsSelectionModel.userID).toEqual(2)
|
||||
}))
|
||||
})
|
||||
|
||||
// GET filterRules
|
||||
|
||||
it('should convert user input to correct filter rules on text field search title + content', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on text field search title + content', () => {
|
||||
component.textFilterInput.nativeElement.value = 'foo'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
fixture.detectChanges()
|
||||
@@ -1135,9 +1140,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on text field search title only', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on text field search title only', () => {
|
||||
component.textFilterInput.nativeElement.value = 'foo'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.query(
|
||||
@@ -1154,9 +1159,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on text field search equals asn', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on text field search equals asn', () => {
|
||||
component.textFilterInput.nativeElement.value = '1234'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1174,9 +1179,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1234',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on text field search greater than asn', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on text field search greater than asn', () => {
|
||||
component.textFilterInput.nativeElement.value = '123'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1198,9 +1203,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '123',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on text field search less than asn', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on text field search less than asn', () => {
|
||||
component.textFilterInput.nativeElement.value = '999'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1222,9 +1227,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '999',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on asn is null', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on asn is null', () => {
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[2]
|
||||
@@ -1242,9 +1247,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'true',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on asn is not null', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on asn is not null', () => {
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(NgbDropdownItem)
|
||||
)[2]
|
||||
@@ -1262,9 +1267,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'false',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on mime type', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on mime type', () => {
|
||||
component.textFilterInput.nativeElement.value = 'pdf'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1280,9 +1285,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'pdf',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on full text query', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on full text query', () => {
|
||||
component.textFilterInput.nativeElement.value = 'foo'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1298,9 +1303,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on tag select not assigned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on tag select not assigned', () => {
|
||||
const tagsFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[0]
|
||||
@@ -1317,9 +1322,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'false',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on tag selections', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on tag selections', () => {
|
||||
const tagsFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[0] // Tags dropdown
|
||||
@@ -1369,9 +1374,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: tags[1].id.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on correspondent selections', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on correspondent selections', () => {
|
||||
const correspondentsFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[1] // Corresp dropdown
|
||||
@@ -1409,9 +1414,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: correspondents[1].id.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on correspondent select not assigned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on correspondent select not assigned', () => {
|
||||
const correspondentsFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[1]
|
||||
@@ -1441,9 +1446,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: NEGATIVE_NULL_FILTER_VALUE.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on document type selections', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on document type selections', () => {
|
||||
const documentTypesFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[2] // DocType dropdown
|
||||
@@ -1481,9 +1486,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: document_types[1].id.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on doc type select not assigned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on doc type select not assigned', () => {
|
||||
const docTypesFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[2]
|
||||
@@ -1513,9 +1518,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: NEGATIVE_NULL_FILTER_VALUE.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on storage path selections', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on storage path selections', () => {
|
||||
const storagePathFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[3] // StoragePath dropdown
|
||||
@@ -1553,9 +1558,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: storage_paths[1].id.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on storage path select not assigned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on storage path select not assigned', () => {
|
||||
const storagePathsFilterableDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(FilterableDropdownComponent)
|
||||
)[3]
|
||||
@@ -1585,9 +1590,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: NEGATIVE_NULL_FILTER_VALUE.toString(),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on custom field selections', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on custom field selections', () => {
|
||||
const customFieldsQueryDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(CustomFieldsQueryDropdownComponent)
|
||||
)[0]
|
||||
@@ -1617,9 +1622,9 @@ describe('FilterEditorComponent', () => {
|
||||
]),
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date created from', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date created from', () => {
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)[0]
|
||||
@@ -1637,9 +1642,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '2023-05-14',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date created to', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date created to', () => {
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)[0]
|
||||
@@ -1657,9 +1662,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '2023-05-14',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date created with relative date', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date created with relative date', () => {
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)[0]
|
||||
@@ -1673,9 +1678,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should carry over text filtering on date created with relative date', fakeAsync(() => {
|
||||
it('should carry over text filtering on date created with relative date', () => {
|
||||
component.textFilter = 'foo'
|
||||
const dateCreatedDropdown = fixture.debugElement.queryAll(
|
||||
By.directive(DatesDropdownComponent)
|
||||
@@ -1690,9 +1695,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo,created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert legacy title filters into full text query when adding a created relative date', fakeAsync(() => {
|
||||
it('should convert legacy title filters into full text query when adding a created relative date', () => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_TITLE,
|
||||
@@ -1712,9 +1717,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo,created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert simple title filters into full text query when adding a created relative date', fakeAsync(() => {
|
||||
it('should convert simple title filters into full text query when adding a created relative date', () => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_SIMPLE_TITLE,
|
||||
@@ -1734,9 +1739,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo,created:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should leave relative dates not in quick list intact', fakeAsync(() => {
|
||||
it('should leave relative dates not in quick list intact', () => {
|
||||
component.textFilterInput.nativeElement.value = 'created:[-2 week to now]'
|
||||
component.textFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
const textFieldTargetDropdown = fixture.debugElement.queryAll(
|
||||
@@ -1762,9 +1767,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'added:[-2 month to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date added after', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date added after', () => {
|
||||
const datesDropdown = fixture.debugElement.query(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)
|
||||
@@ -1782,9 +1787,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '2023-05-14',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date added before', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date added before', () => {
|
||||
const datesDropdown = fixture.debugElement.query(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)
|
||||
@@ -1802,9 +1807,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '2023-05-14',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter rules on date added with relative date', fakeAsync(() => {
|
||||
it('should convert user input to correct filter rules on date added with relative date', () => {
|
||||
const datesDropdown = fixture.debugElement.query(
|
||||
By.directive(DatesDropdownComponent)
|
||||
)
|
||||
@@ -1818,9 +1823,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'added:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should carry over text filtering on date added with relative date', fakeAsync(() => {
|
||||
it('should carry over text filtering on date added with relative date', () => {
|
||||
component.textFilter = 'foo'
|
||||
const datesDropdown = fixture.debugElement.query(
|
||||
By.directive(DatesDropdownComponent)
|
||||
@@ -1835,9 +1840,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo,added:[-1 week to now]',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select my docs', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select my docs', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1851,9 +1856,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select shared with me', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select shared with me', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1866,9 +1871,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select shared with me', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select shared with me', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1889,9 +1894,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1,2',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select shared by me', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select shared by me', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1904,9 +1909,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '1',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select unowned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select unowned', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1919,9 +1924,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'true',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions select others', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions select others', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1940,9 +1945,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: '3',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should convert user input to correct filter on permissions hide unowned', fakeAsync(() => {
|
||||
it('should convert user input to correct filter on permissions hide unowned', () => {
|
||||
const permissionsDropdown = fixture.debugElement.query(
|
||||
By.directive(PermissionsFilterDropdownComponent)
|
||||
)
|
||||
@@ -1960,7 +1965,7 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'false',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
// The rest
|
||||
|
||||
@@ -2221,7 +2226,7 @@ describe('FilterEditorComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should keep deprecated custom fields target available for legacy filters', fakeAsync(() => {
|
||||
it('should keep deprecated custom fields target available for legacy filters', () => {
|
||||
component.filterRules = [
|
||||
{
|
||||
rule_type: FILTER_CUSTOM_FIELDS_TEXT,
|
||||
@@ -2242,9 +2247,9 @@ describe('FilterEditorComponent', () => {
|
||||
value: 'foo',
|
||||
},
|
||||
])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should call autocomplete endpoint on input', fakeAsync(() => {
|
||||
it('should call autocomplete endpoint on input', () => {
|
||||
component.textFilterTarget = 'fulltext-query' // TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
const autocompleteSpy = jest.spyOn(searchService, 'autocomplete')
|
||||
component.searchAutoComplete(of('hello')).subscribe()
|
||||
@@ -2254,9 +2259,9 @@ describe('FilterEditorComponent', () => {
|
||||
component.searchAutoComplete(of('hello world 1')).subscribe()
|
||||
tick(250)
|
||||
expect(autocompleteSpy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should handle autocomplete backend failure gracefully', fakeAsync(() => {
|
||||
it('should handle autocomplete backend failure gracefully', () => {
|
||||
component.textFilterTarget = 'fulltext-query' // TEXT_FILTER_TARGET_FULLTEXT_QUERY
|
||||
const serviceAutocompleteSpy = jest.spyOn(searchService, 'autocomplete')
|
||||
serviceAutocompleteSpy.mockReturnValue(
|
||||
@@ -2270,7 +2275,7 @@ describe('FilterEditorComponent', () => {
|
||||
tick(250)
|
||||
expect(serviceAutocompleteSpy).toHaveBeenCalled()
|
||||
expect(result).toEqual([])
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support choosing a autocomplete item', () => {
|
||||
expect(component.textFilter).toBeNull()
|
||||
|
||||
+5
-10
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { NgbActiveModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -22,8 +17,8 @@ describe('SaveViewConfigDialogComponent', () => {
|
||||
let fixture: ComponentFixture<SaveViewConfigDialogComponent>
|
||||
let modal: NgbActiveModal
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
providers: [
|
||||
NgbActiveModal,
|
||||
{
|
||||
@@ -56,8 +51,8 @@ describe('SaveViewConfigDialogComponent', () => {
|
||||
fixture = TestBed.createComponent(SaveViewConfigDialogComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
tick()
|
||||
}))
|
||||
await fixture.whenStable()
|
||||
})
|
||||
|
||||
it('should support default name', () => {
|
||||
const name = 'Tag: Inbox'
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
discardPeriodicTasks,
|
||||
fakeAsync,
|
||||
flush,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
@@ -24,8 +17,18 @@ describe('FileDropComponent', () => {
|
||||
let settingsService: SettingsService
|
||||
let uploadDocumentsService: UploadDocumentsService
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
const advanceTimers = (ms: number) => {
|
||||
jest.advanceTimersByTime(ms)
|
||||
fixture.detectChanges()
|
||||
}
|
||||
|
||||
const flushPromises = async () => {
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [FileDropComponent, ToastsComponent],
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
@@ -43,6 +46,10 @@ describe('FileDropComponent', () => {
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should enable drag-drop if user has permissions', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
expect(component.dragDropEnabled).toBeTruthy()
|
||||
@@ -53,22 +60,21 @@ describe('FileDropComponent', () => {
|
||||
expect(component.dragDropEnabled).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should disable drag-drop if disabled in settings', fakeAsync(() => {
|
||||
it('should disable drag-drop if disabled in settings', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
settingsService.globalDropzoneEnabled.set(false)
|
||||
expect(component.dragDropEnabled).toBeFalsy()
|
||||
|
||||
component.onDragOver(new Event('dragover') as DragEvent)
|
||||
tick(1)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(1)
|
||||
expect(component.fileIsOver()).toBeFalsy()
|
||||
const dropzone = fixture.debugElement.query(
|
||||
By.css('.global-dropzone-overlay')
|
||||
)
|
||||
expect(dropzone.classes['active']).toBeFalsy()
|
||||
component.onDragLeave(new Event('dragleave') as DragEvent)
|
||||
tick(700)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(700)
|
||||
// drop
|
||||
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile')
|
||||
const dragEvent = new Event('drop')
|
||||
@@ -79,22 +85,21 @@ describe('FileDropComponent', () => {
|
||||
},
|
||||
}
|
||||
component.onDrop(dragEvent as DragEvent)
|
||||
tick(3000)
|
||||
advanceTimers(3000)
|
||||
expect(uploadSpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support drag drop, initiate upload', fakeAsync(() => {
|
||||
it('should support drag drop, initiate upload', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
expect(component.fileIsOver()).toBeFalsy()
|
||||
const overEvent = new Event('dragover') as DragEvent
|
||||
;(overEvent as any).dataTransfer = { types: ['Files'] }
|
||||
component.onDragOver(overEvent)
|
||||
tick(1)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(1)
|
||||
expect(component.fileIsOver()).toBeTruthy()
|
||||
component.onDragLeave(new Event('dragleave') as DragEvent)
|
||||
tick(700)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(700)
|
||||
// drop
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile')
|
||||
@@ -113,24 +118,22 @@ describe('FileDropComponent', () => {
|
||||
],
|
||||
}
|
||||
component.onDrop(dragEvent as DragEvent)
|
||||
tick(3000)
|
||||
advanceTimers(3000)
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(uploadSpy).toHaveBeenCalled()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should support drag drop, initiate upload with webkitGetAsEntry', fakeAsync(() => {
|
||||
it('should support drag drop, initiate upload with webkitGetAsEntry', async () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
expect(component.fileIsOver()).toBeFalsy()
|
||||
const overEvent = new Event('dragover') as DragEvent
|
||||
;(overEvent as any).dataTransfer = { types: ['Files'] }
|
||||
component.onDragOver(overEvent)
|
||||
tick(1)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(1)
|
||||
expect(component.fileIsOver()).toBeTruthy()
|
||||
component.onDragLeave(new Event('dragleave') as DragEvent)
|
||||
tick(700)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(700)
|
||||
// drop
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile')
|
||||
@@ -154,13 +157,14 @@ describe('FileDropComponent', () => {
|
||||
files: [],
|
||||
}
|
||||
component.onDrop(dragEvent as DragEvent)
|
||||
tick(3000)
|
||||
await flushPromises()
|
||||
advanceTimers(3000)
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(uploadSpy).toHaveBeenCalled()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should show an error on traverseFileTree error', fakeAsync(() => {
|
||||
it('should show an error on traverseFileTree error', async () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
const traverseSpy = jest
|
||||
@@ -190,28 +194,25 @@ describe('FileDropComponent', () => {
|
||||
|
||||
component.onDrop(event)
|
||||
|
||||
tick() // flush microtasks (e.g., Promise.reject)
|
||||
await flushPromises()
|
||||
|
||||
expect(traverseSpy).toHaveBeenCalled()
|
||||
expect(toastSpy).toHaveBeenCalledWith(
|
||||
$localize`Failed to read dropped items: Error traversing file tree`
|
||||
)
|
||||
})
|
||||
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
|
||||
it('should support drag drop, initiate upload without DataTransfer API support', fakeAsync(() => {
|
||||
it('should support drag drop, initiate upload without DataTransfer API support', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
expect(component.fileIsOver()).toBeFalsy()
|
||||
const overEvent = new Event('dragover') as DragEvent
|
||||
;(overEvent as any).dataTransfer = { types: ['Files'] }
|
||||
component.onDragOver(overEvent)
|
||||
tick(1)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(1)
|
||||
expect(component.fileIsOver()).toBeTruthy()
|
||||
component.onDragLeave(new Event('dragleave') as DragEvent)
|
||||
tick(700)
|
||||
fixture.detectChanges()
|
||||
advanceTimers(700)
|
||||
// drop
|
||||
const toastSpy = jest.spyOn(toastService, 'show')
|
||||
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile')
|
||||
@@ -225,11 +226,10 @@ describe('FileDropComponent', () => {
|
||||
files: [file],
|
||||
}
|
||||
component.onDrop(dragEvent as DragEvent)
|
||||
tick(3000)
|
||||
advanceTimers(3000)
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
expect(uploadSpy).toHaveBeenCalled()
|
||||
discardPeriodicTasks()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should resolve a single file when entry isFile', () => {
|
||||
const mockFile = new File(['data'], 'test.txt', { type: 'text/plain' })
|
||||
@@ -295,7 +295,7 @@ describe('FileDropComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should ignore events if disabled', fakeAsync(() => {
|
||||
it('should ignore events if disabled', () => {
|
||||
settingsService.globalDropzoneEnabled.set(false)
|
||||
expect(settingsService.globalDropzoneActive()).toBeFalsy()
|
||||
component.onDragOver(new Event('dragover') as DragEvent)
|
||||
@@ -305,37 +305,37 @@ describe('FileDropComponent', () => {
|
||||
expect(settingsService.globalDropzoneActive()).toBeTruthy()
|
||||
component.onDrop(new Event('drop') as DragEvent)
|
||||
expect(settingsService.globalDropzoneActive()).toBeTruthy()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should hide if app loses focus', fakeAsync(() => {
|
||||
it('should hide if app loses focus', () => {
|
||||
jest.useFakeTimers()
|
||||
const leaveSpy = jest.spyOn(component, 'onDragLeave')
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
settingsService.globalDropzoneEnabled.set(true)
|
||||
const overEvent = new Event('dragover') as DragEvent
|
||||
;(overEvent as any).dataTransfer = { types: ['Files'] }
|
||||
component.onDragOver(overEvent)
|
||||
tick(1)
|
||||
advanceTimers(1)
|
||||
expect(component.hidden()).toBeFalsy()
|
||||
expect(component.fileIsOver()).toBeTruthy()
|
||||
jest.spyOn(document, 'hidden', 'get').mockReturnValue(true)
|
||||
component.onVisibilityChange()
|
||||
expect(leaveSpy).toHaveBeenCalled()
|
||||
flush()
|
||||
}))
|
||||
})
|
||||
|
||||
it('should hide on window blur', fakeAsync(() => {
|
||||
it('should hide on window blur', () => {
|
||||
jest.useFakeTimers()
|
||||
const leaveSpy = jest.spyOn(component, 'onDragLeave')
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
settingsService.globalDropzoneEnabled.set(true)
|
||||
const overEvent = new Event('dragover') as DragEvent
|
||||
;(overEvent as any).dataTransfer = { types: ['Files'] }
|
||||
component.onDragOver(overEvent)
|
||||
tick(1)
|
||||
advanceTimers(1)
|
||||
expect(component.hidden()).toBeFalsy()
|
||||
expect(component.fileIsOver()).toBeTruthy()
|
||||
jest.spyOn(document, 'hidden', 'get').mockReturnValue(true)
|
||||
component.onWindowBlur()
|
||||
expect(leaveSpy).toHaveBeenCalled()
|
||||
flush()
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
+17
-22
@@ -5,12 +5,7 @@ import {
|
||||
withInterceptorsFromDi,
|
||||
} from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
fakeAsync,
|
||||
tick,
|
||||
} from '@angular/core/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { RouterLinkWithHref } from '@angular/router'
|
||||
@@ -140,24 +135,26 @@ describe('ManagementListComponent', () => {
|
||||
|
||||
// These tests are shared among all management list components
|
||||
|
||||
it('should support filtering, clear on Esc key', fakeAsync(() => {
|
||||
it('should support filtering, clear on Esc key', () => {
|
||||
jest.useFakeTimers()
|
||||
const nameFilterInput = fixture.debugElement.query(By.css('input'))
|
||||
nameFilterInput.nativeElement.value = 'foo'
|
||||
// nameFilterInput.nativeElement.dispatchEvent(new Event('input'))
|
||||
component.nameFilter = 'foo' // subject normally triggered by ngModel
|
||||
tick(400) // debounce
|
||||
jest.advanceTimersByTime(400) // debounce
|
||||
fixture.detectChanges()
|
||||
expect(component.data()).toEqual([tags[0]])
|
||||
|
||||
nameFilterInput.nativeElement.dispatchEvent(
|
||||
new KeyboardEvent('keyup', { code: 'Escape' })
|
||||
)
|
||||
tick(400) // debounce
|
||||
jest.advanceTimersByTime(400) // debounce
|
||||
fixture.detectChanges()
|
||||
expect(component.nameFilter).toBeNull()
|
||||
expect(component.data()).toEqual(tags)
|
||||
tick(100) // load
|
||||
}))
|
||||
jest.advanceTimersByTime(100) // load
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support create, show notification on error / success', () => {
|
||||
let modal: NgbModalRef
|
||||
@@ -230,7 +227,8 @@ describe('ManagementListComponent', () => {
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should use API count for pagination and nested ids for displayed total', fakeAsync(() => {
|
||||
it('should use API count for pagination and nested ids for displayed total', () => {
|
||||
jest.useFakeTimers()
|
||||
jest.spyOn(tagService, 'listFiltered').mockReturnValueOnce(
|
||||
of({
|
||||
count: 1,
|
||||
@@ -240,11 +238,12 @@ describe('ManagementListComponent', () => {
|
||||
)
|
||||
|
||||
component.reloadData()
|
||||
tick(100)
|
||||
jest.advanceTimersByTime(100)
|
||||
|
||||
expect(component.collectionSize()).toBe(1)
|
||||
expect(component.displayCollectionSize()).toBe(3)
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('should support quick filter for objects', () => {
|
||||
const expectedUrl = documentListViewService.getQuickFilterUrl([
|
||||
@@ -525,7 +524,7 @@ describe('ManagementListComponent', () => {
|
||||
expect(component.pageSize).toBe(25)
|
||||
})
|
||||
|
||||
it('pageSize setter should update settings, reset page and reload data on success', fakeAsync(() => {
|
||||
it('pageSize setter should update settings, reset page and reload data on success', () => {
|
||||
const reloadSpy = jest.spyOn(component, 'reloadData')
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
|
||||
@@ -539,8 +538,6 @@ describe('ManagementListComponent', () => {
|
||||
component.page.set(2)
|
||||
component.pageSize = 100
|
||||
|
||||
tick()
|
||||
|
||||
expect(settingsService.set).toHaveBeenCalledWith(
|
||||
SETTINGS_KEYS.OBJECT_LIST_SIZES,
|
||||
{ tags: 100 }
|
||||
@@ -548,9 +545,9 @@ describe('ManagementListComponent', () => {
|
||||
expect(component.page()).toBe(1)
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
expect(toastErrorSpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
|
||||
it('pageSize setter should show error toast on settings store failure', fakeAsync(() => {
|
||||
it('pageSize setter should show error toast on settings store failure', () => {
|
||||
const reloadSpy = jest.spyOn(component, 'reloadData')
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
|
||||
@@ -563,12 +560,10 @@ describe('ManagementListComponent', () => {
|
||||
component.typeNamePlural = 'tags'
|
||||
component.pageSize = 50
|
||||
|
||||
tick()
|
||||
|
||||
expect(toastErrorSpy).toHaveBeenCalledWith(
|
||||
'Error saving settings',
|
||||
expect.any(Error)
|
||||
)
|
||||
expect(reloadSpy).not.toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -109,6 +109,7 @@ describe('DocumentListViewService', () => {
|
||||
documentListViewService = TestBed.inject(DocumentListViewService)
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
router = TestBed.inject(Router)
|
||||
jest.spyOn(router, 'navigate').mockResolvedValue(true)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
HttpTestingController,
|
||||
provideHttpClientTesting,
|
||||
} from '@angular/common/http/testing'
|
||||
import { fakeAsync, TestBed, tick } from '@angular/core/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
@@ -122,7 +122,8 @@ describe('SettingsService', () => {
|
||||
expect(req.request.method).toEqual('GET')
|
||||
})
|
||||
|
||||
it('should catch error and show toast on retrieve ui_settings error', fakeAsync(() => {
|
||||
it('should catch error and show toast on retrieve ui_settings error', () => {
|
||||
jest.useFakeTimers()
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
httpTestingController
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
@@ -130,9 +131,10 @@ describe('SettingsService', () => {
|
||||
{ detail: 'You do not have permission to perform this action.' },
|
||||
{ status: 403, statusText: 'Forbidden' }
|
||||
)
|
||||
tick(500)
|
||||
jest.advanceTimersByTime(500)
|
||||
expect(toastSpy).toHaveBeenCalled()
|
||||
}))
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('calls ui_settings api endpoint with POST on store', () => {
|
||||
let req = httpTestingController.expectOne(
|
||||
|
||||
@@ -13,12 +13,3 @@ export const environment = {
|
||||
webSocketProtocol: 'ws:',
|
||||
webSocketBaseUrl: '/ws/',
|
||||
}
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
||||
|
||||
@@ -6,11 +6,6 @@ import '@angular/localize/init'
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
@@ -18,34 +13,6 @@ import '@angular/localize/init'
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user