]> git.ipfire.org Git - thirdparty/paperless-ngx.git/blob
930164dce132b6b8e96a17cbdd32a6c765a94f55
[thirdparty/paperless-ngx.git] /
1 import { CdkDragDrop } from '@angular/cdk/drag-drop'
2 import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
3 import { provideHttpClientTesting } from '@angular/common/http/testing'
4 import { ComponentFixture, TestBed } from '@angular/core/testing'
5 import {
6 FormControl,
7 FormGroup,
8 FormsModule,
9 ReactiveFormsModule,
10 } from '@angular/forms'
11 import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
12 import { NgSelectModule } from '@ng-select/ng-select'
13 import { of } from 'rxjs'
14 import { CustomFieldDataType } from 'src/app/data/custom-field'
15 import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
16 import { Workflow } from 'src/app/data/workflow'
17 import {
18 WorkflowAction,
19 WorkflowActionType,
20 } from 'src/app/data/workflow-action'
21 import {
22 DocumentSource,
23 WorkflowTriggerType,
24 } from 'src/app/data/workflow-trigger'
25 import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
26 import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
27 import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
28 import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
29 import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
30 import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
31 import { MailRuleService } from 'src/app/services/rest/mail-rule.service'
32 import { StoragePathService } from 'src/app/services/rest/storage-path.service'
33 import { SettingsService } from 'src/app/services/settings.service'
34 import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component'
35 import { NumberComponent } from '../../input/number/number.component'
36 import { PermissionsGroupComponent } from '../../input/permissions/permissions-group/permissions-group.component'
37 import { PermissionsUserComponent } from '../../input/permissions/permissions-user/permissions-user.component'
38 import { SelectComponent } from '../../input/select/select.component'
39 import { SwitchComponent } from '../../input/switch/switch.component'
40 import { TagsComponent } from '../../input/tags/tags.component'
41 import { TextComponent } from '../../input/text/text.component'
42 import { EditDialogMode } from '../edit-dialog.component'
43 import {
44 DOCUMENT_SOURCE_OPTIONS,
45 SCHEDULE_DATE_FIELD_OPTIONS,
46 WORKFLOW_ACTION_OPTIONS,
47 WORKFLOW_TYPE_OPTIONS,
48 WorkflowEditDialogComponent,
49 } from './workflow-edit-dialog.component'
50
51 const workflow: Workflow = {
52 name: 'Workflow 1',
53 id: 1,
54 order: 1,
55 enabled: true,
56 triggers: [
57 {
58 id: 1,
59 type: WorkflowTriggerType.Consumption,
60 sources: [DocumentSource.ConsumeFolder],
61 filter_filename: '*',
62 },
63 ],
64 actions: [
65 {
66 id: 1,
67 type: WorkflowActionType.Assignment,
68 assign_title: 'foo',
69 },
70 {
71 id: 4,
72 type: WorkflowActionType.Assignment,
73 assign_owner: 2,
74 },
75 ],
76 }
77
78 describe('WorkflowEditDialogComponent', () => {
79 let component: WorkflowEditDialogComponent
80 let settingsService: SettingsService
81 let fixture: ComponentFixture<WorkflowEditDialogComponent>
82
83 beforeEach(() => {
84 TestBed.configureTestingModule({
85 imports: [
86 FormsModule,
87 ReactiveFormsModule,
88 NgSelectModule,
89 NgbModule,
90 WorkflowEditDialogComponent,
91 IfPermissionsDirective,
92 IfOwnerDirective,
93 SelectComponent,
94 TextComponent,
95 NumberComponent,
96 SwitchComponent,
97 TagsComponent,
98 PermissionsUserComponent,
99 PermissionsGroupComponent,
100 SafeHtmlPipe,
101 ConfirmButtonComponent,
102 ],
103 providers: [
104 NgbActiveModal,
105 {
106 provide: CorrespondentService,
107 useValue: {
108 listAll: () =>
109 of({
110 results: [
111 {
112 id: 1,
113 username: 'c1',
114 },
115 ],
116 }),
117 },
118 },
119 {
120 provide: DocumentTypeService,
121 useValue: {
122 listAll: () =>
123 of({
124 results: [
125 {
126 id: 1,
127 username: 'dt1',
128 },
129 ],
130 }),
131 },
132 },
133 {
134 provide: StoragePathService,
135 useValue: {
136 listAll: () =>
137 of({
138 results: [
139 {
140 id: 1,
141 username: 'sp1',
142 },
143 ],
144 }),
145 },
146 },
147 {
148 provide: MailRuleService,
149 useValue: {
150 listAll: () =>
151 of({
152 results: [],
153 }),
154 },
155 },
156 {
157 provide: CustomFieldsService,
158 useValue: {
159 listAll: () =>
160 of({
161 results: [
162 {
163 id: 1,
164 name: 'cf1',
165 data_type: CustomFieldDataType.String,
166 },
167 {
168 id: 2,
169 name: 'cf2',
170 data_type: CustomFieldDataType.Date,
171 },
172 ],
173 }),
174 },
175 },
176 provideHttpClient(withInterceptorsFromDi()),
177 provideHttpClientTesting(),
178 ],
179 }).compileComponents()
180
181 fixture = TestBed.createComponent(WorkflowEditDialogComponent)
182 settingsService = TestBed.inject(SettingsService)
183 settingsService.currentUser = { id: 99, username: 'user99' }
184 component = fixture.componentInstance
185
186 fixture.detectChanges()
187 })
188
189 it('should support create and edit modes, support adding triggers and actions on new workflow', () => {
190 component.dialogMode = EditDialogMode.CREATE
191 const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
192 const editTitleSpy = jest.spyOn(component, 'getEditTitle')
193 fixture.detectChanges()
194 expect(createTitleSpy).toHaveBeenCalled()
195 expect(editTitleSpy).not.toHaveBeenCalled()
196 expect(component.object).toBeUndefined()
197 component.addAction()
198 expect(component.object).not.toBeUndefined()
199 expect(component.object.actions).toHaveLength(1)
200 component.object = undefined
201 component.addTrigger()
202 expect(component.object).not.toBeUndefined()
203 expect(component.object.triggers).toHaveLength(1)
204
205 component.dialogMode = EditDialogMode.EDIT
206 fixture.detectChanges()
207 expect(editTitleSpy).toHaveBeenCalled()
208 })
209
210 it('should return source options, type options, type name, schedule date field options', () => {
211 jest.spyOn(settingsService, 'get').mockReturnValue(true)
212 component.ngOnInit()
213 expect(component.sourceOptions).toEqual(DOCUMENT_SOURCE_OPTIONS)
214 expect(component.triggerTypeOptions).toEqual(WORKFLOW_TYPE_OPTIONS)
215 expect(
216 component.getTriggerTypeOptionName(WorkflowTriggerType.DocumentAdded)
217 ).toEqual('Document Added')
218 expect(component.getTriggerTypeOptionName(null)).toEqual('')
219 expect(component.sourceOptions).toEqual(DOCUMENT_SOURCE_OPTIONS)
220 expect(component.actionTypeOptions).toEqual(WORKFLOW_ACTION_OPTIONS)
221 expect(
222 component.getActionTypeOptionName(WorkflowActionType.Assignment)
223 ).toEqual('Assignment')
224 expect(component.getActionTypeOptionName(null)).toEqual('')
225 expect(component.scheduleDateFieldOptions).toEqual(
226 SCHEDULE_DATE_FIELD_OPTIONS
227 )
228
229 // Email disabled
230 jest.spyOn(settingsService, 'get').mockReturnValue(false)
231 component.ngOnInit()
232 expect(component.actionTypeOptions).toEqual(
233 WORKFLOW_ACTION_OPTIONS.filter((a) => a.id !== WorkflowActionType.Email)
234 )
235 })
236
237 it('should support add and remove triggers and actions', () => {
238 component.object = workflow
239 component.addTrigger()
240 expect(component.object.triggers.length).toEqual(2)
241 component.addAction()
242 expect(component.object.actions.length).toEqual(3)
243 component.removeTrigger(1)
244 expect(component.object.triggers.length).toEqual(1)
245 component.removeAction(1)
246 expect(component.object.actions.length).toEqual(2)
247 })
248
249 it('should update order and remove ids from actions on drag n drop', () => {
250 const action1 = workflow.actions[0]
251 const action2 = workflow.actions[1]
252 component.object = workflow
253 component.ngOnInit()
254 component.onActionDrop({ previousIndex: 0, currentIndex: 1 } as CdkDragDrop<
255 WorkflowAction[]
256 >)
257 expect(component.object.actions).toEqual([action2, action1])
258 expect(action1.id).toBeNull()
259 expect(action2.id).toBeNull()
260 })
261
262 it('should not include auto matching in algorithms', () => {
263 expect(component.getMatchingAlgorithms()).not.toContain(
264 MATCHING_ALGORITHMS.find((a) => a.id === MATCH_AUTO)
265 )
266 })
267
268 it('should disable or enable action fields based on removal action type', () => {
269 const workflow: Workflow = {
270 name: 'Workflow 1',
271 id: 1,
272 order: 1,
273 enabled: true,
274 triggers: [],
275 actions: [
276 {
277 id: 1,
278 type: WorkflowActionType.Removal,
279 remove_all_tags: true,
280 remove_all_document_types: true,
281 remove_all_correspondents: true,
282 remove_all_storage_paths: true,
283 remove_all_custom_fields: true,
284 remove_all_owners: true,
285 remove_all_permissions: true,
286 },
287 ],
288 }
289 component.object = workflow
290 component.ngOnInit()
291
292 component['checkRemovalActionFields'](workflow)
293
294 // Assert that the action fields are disabled or enabled correctly
295 expect(
296 component.actionFields.at(0).get('remove_tags').disabled
297 ).toBeTruthy()
298 expect(
299 component.actionFields.at(0).get('remove_document_types').disabled
300 ).toBeTruthy()
301 expect(
302 component.actionFields.at(0).get('remove_correspondents').disabled
303 ).toBeTruthy()
304 expect(
305 component.actionFields.at(0).get('remove_storage_paths').disabled
306 ).toBeTruthy()
307 expect(
308 component.actionFields.at(0).get('remove_custom_fields').disabled
309 ).toBeTruthy()
310 expect(
311 component.actionFields.at(0).get('remove_owners').disabled
312 ).toBeTruthy()
313 expect(
314 component.actionFields.at(0).get('remove_view_users').disabled
315 ).toBeTruthy()
316 expect(
317 component.actionFields.at(0).get('remove_view_groups').disabled
318 ).toBeTruthy()
319 expect(
320 component.actionFields.at(0).get('remove_change_users').disabled
321 ).toBeTruthy()
322 expect(
323 component.actionFields.at(0).get('remove_change_groups').disabled
324 ).toBeTruthy()
325
326 workflow.actions[0].remove_all_tags = false
327 workflow.actions[0].remove_all_document_types = false
328 workflow.actions[0].remove_all_correspondents = false
329 workflow.actions[0].remove_all_storage_paths = false
330 workflow.actions[0].remove_all_custom_fields = false
331 workflow.actions[0].remove_all_owners = false
332 workflow.actions[0].remove_all_permissions = false
333
334 component['checkRemovalActionFields'](workflow)
335
336 // Assert that the action fields are disabled or enabled correctly
337 expect(component.actionFields.at(0).get('remove_tags').disabled).toBeFalsy()
338 expect(
339 component.actionFields.at(0).get('remove_document_types').disabled
340 ).toBeFalsy()
341 expect(
342 component.actionFields.at(0).get('remove_correspondents').disabled
343 ).toBeFalsy()
344 expect(
345 component.actionFields.at(0).get('remove_storage_paths').disabled
346 ).toBeFalsy()
347 expect(
348 component.actionFields.at(0).get('remove_custom_fields').disabled
349 ).toBeFalsy()
350 expect(
351 component.actionFields.at(0).get('remove_owners').disabled
352 ).toBeFalsy()
353 expect(
354 component.actionFields.at(0).get('remove_view_users').disabled
355 ).toBeFalsy()
356 expect(
357 component.actionFields.at(0).get('remove_view_groups').disabled
358 ).toBeFalsy()
359 expect(
360 component.actionFields.at(0).get('remove_change_users').disabled
361 ).toBeFalsy()
362 expect(
363 component.actionFields.at(0).get('remove_change_groups').disabled
364 ).toBeFalsy()
365 })
366
367 it('should prune empty nested objects on save', () => {
368 component.object = workflow
369 component.addTrigger()
370 component.addAction()
371 expect(component.objectForm.get('actions').value[0].email).not.toBeNull()
372 expect(component.objectForm.get('actions').value[0].webhook).not.toBeNull()
373 component.save()
374 expect(component.objectForm.get('actions').value[0].email).toBeNull()
375 expect(component.objectForm.get('actions').value[0].webhook).toBeNull()
376 })
377
378 it('should remove selected custom field from the form group', () => {
379 const formGroup = new FormGroup({
380 assign_custom_fields: new FormControl([1, 2, 3]),
381 })
382
383 component.removeSelectedCustomField(2, formGroup)
384 expect(formGroup.get('assign_custom_fields').value).toEqual([1, 3])
385
386 component.removeSelectedCustomField(1, formGroup)
387 expect(formGroup.get('assign_custom_fields').value).toEqual([3])
388
389 component.removeSelectedCustomField(3, formGroup)
390 expect(formGroup.get('assign_custom_fields').value).toEqual([])
391 })
392 })