Skip to content

Commit 6a5c4ad

Browse files
test: intacct export settings edge cases and utility functions (#1003)
* test: intacct export settings initialization * test: intacct export settings save functionality + misc tests * test: intacct export settings watchers * test: intacct export settings edge cases and utility functions * refactor: linting
1 parent cd49a8d commit 6a5c4ad

File tree

1 file changed

+325
-10
lines changed

1 file changed

+325
-10
lines changed

src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.spec.ts

Lines changed: 325 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import { EmployeeFieldMapping, ExpenseGroupingFieldOption, ExportDateType, FyleF
1414
import { ExportSettingOptionSearch, ExportSettingModel } from 'src/app/core/models/common/export-settings.model';
1515
import { IntacctDestinationAttribute, PaginatedintacctDestinationAttribute } from 'src/app/core/models/intacct/db/destination-attribute.model';
1616
import { SharedModule } from 'src/app/shared/shared.module';
17-
import { brandingConfig } from 'src/app/branding/branding-config';
17+
import { brandingConfig, brandingContent } from 'src/app/branding/branding-config';
1818
import { BrandingConfiguration } from 'src/app/core/models/branding/branding-configuration.model';
19-
import { ExportSettingModel as IntacctExportSettingModel } from 'src/app/core/models/intacct/intacct-configuration/export-settings.model';
19+
import { ExportSettingGet, ExportSettingModel as IntacctExportSettingModel } from 'src/app/core/models/intacct/intacct-configuration/export-settings.model';
20+
import { TitleCasePipe } from '@angular/common';
21+
2022

2123
describe('IntacctExportSettingsComponent', () => {
2224
let component: IntacctExportSettingsComponent;
@@ -171,16 +173,329 @@ describe('IntacctExportSettingsComponent', () => {
171173
});
172174
});
173175

174-
it('should handle refresh dimensions', () => {
175-
component.refreshDimensions(true);
176-
expect(mappingService.refreshSageIntacctDimensions).toHaveBeenCalled();
177-
expect(mappingService.refreshFyleDimensions).toHaveBeenCalled();
178-
expect(toastService.displayToastMessage).toHaveBeenCalledWith(ToastSeverity.SUCCESS, 'Syncing data dimensions from Sage Intacct');
176+
describe('Utility Functions', () => {
177+
it('should handle refresh dimensions', () => {
178+
component.refreshDimensions(true);
179+
expect(mappingService.refreshSageIntacctDimensions).toHaveBeenCalled();
180+
expect(mappingService.refreshFyleDimensions).toHaveBeenCalled();
181+
expect(toastService.displayToastMessage).toHaveBeenCalledWith(ToastSeverity.SUCCESS, 'Syncing data dimensions from Sage Intacct');
182+
});
183+
184+
it('should navigate to previous step', () => {
185+
component.navigateToPreviousStep();
186+
expect(router.navigate).toHaveBeenCalledWith(['/integrations/intacct/onboarding/connector']);
187+
});
188+
189+
it('should return the correct employee field mapping', () => {
190+
fixture.detectChanges();
191+
192+
expect(
193+
component.getEmployeeFieldMapping(FyleField.VENDOR, IntacctReimbursableExpensesObject.BILL)
194+
).toBe('Vendor');
195+
196+
expect(component.getEmployeeFieldMapping(null, IntacctReimbursableExpensesObject.JOURNAL_ENTRY))
197+
.toBe(new TitleCasePipe().transform(component.exportSettingsForm.get('employeeFieldMapping')?.value));
198+
199+
expect(component.getEmployeeFieldMapping(null, IntacctReimbursableExpensesObject.BILL))
200+
.toBe('Vendor');
201+
});
202+
203+
it('should get the correct export type', () => {
204+
expect(component.getExportType(IntacctReimbursableExpensesObject.JOURNAL_ENTRY)).toBe('Journal_entry');
205+
expect(component.getExportType(IntacctReimbursableExpensesObject.BILL)).toBe('Bill');
206+
expect(component.getExportType(IntacctReimbursableExpensesObject.EXPENSE_REPORT)).toBe('Expense_report');
207+
expect(component.getExportType(null)).toBe('export');
208+
});
209+
});
210+
211+
describe('Watchers', () => {
212+
beforeEach(() => {
213+
fixture.detectChanges();
214+
});
215+
216+
describe('Reimbursable Expense Toggle Watcher', () => {
217+
it('should enable fields on enabling reimbursable expenses', fakeAsync(() => {
218+
component.exportSettingsForm.get('reimbursableExpense')?.setValue(true);
219+
tick();
220+
221+
expect(component.exportSettingsForm.get('reimbursableExportType')?.hasValidator(Validators.required)).toBeTrue();
222+
expect(component.exportSettingsForm.get('reimbursableExportGroup')?.hasValidator(Validators.required)).toBeTrue();
223+
expect(component.exportSettingsForm.get('reimbursableExportDate')?.hasValidator(Validators.required)).toBeTrue();
224+
}));
225+
226+
it('should disable fields on disabling reimbursable expenses', fakeAsync(() => {
227+
component.exportSettingsForm.get('reimbursableExpense')?.setValue(false);
228+
tick();
229+
230+
expect(component.exportSettingsForm.get('reimbursableExportType')?.hasValidator(Validators.required)).toBeFalse();
231+
expect(component.exportSettingsForm.get('reimbursableExportGroup')?.hasValidator(Validators.required)).toBeFalse();
232+
expect(component.exportSettingsForm.get('reimbursableExportDate')?.hasValidator(Validators.required)).toBeFalse();
233+
expect(component.exportSettingsForm.get('reimbursableExportType')?.value).toBeNull();
234+
}));
235+
});
236+
237+
describe('Reimbursable Export Type Watchers', () => {
238+
239+
it('should handle reimbursableExportType being changed to Journal Entry', fakeAsync(() => {
240+
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.JOURNAL_ENTRY);
241+
tick();
242+
243+
expect(component.exportSettingsForm.get('glAccount')?.hasValidator(Validators.required)).toBeTrue();
244+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.enabled).toBeTrue();
245+
}));
246+
247+
it('should handle reimbursableExportType being changed to Expense Report', fakeAsync(() => {
248+
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.EXPENSE_REPORT);
249+
tick();
250+
251+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.EMPLOYEE);
252+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.disabled).toBeTrue();
253+
}));
254+
255+
it('should handle reimbursableExportType being changed to Bill', fakeAsync(() => {
256+
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.BILL);
257+
tick();
258+
259+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.VENDOR);
260+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.disabled).toBeTrue();
261+
}));
262+
});
263+
264+
describe('Credit Card Expense Toggle Watcher', () => {
265+
it('should enable fields on enabling CCC expenses', fakeAsync(() => {
266+
component.exportSettingsForm.get('creditCardExpense')?.setValue(true);
267+
tick();
268+
269+
expect(component.exportSettingsForm.get('cccExportType')?.hasValidator(Validators.required)).toBeTrue();
270+
expect(component.exportSettingsForm.get('cccExportGroup')?.hasValidator(Validators.required)).toBeTrue();
271+
expect(component.exportSettingsForm.get('cccExportDate')?.hasValidator(Validators.required)).toBeTrue();
272+
}));
273+
274+
it('should disable fields on disabling CCC expenses', fakeAsync(() => {
275+
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);
276+
tick();
277+
278+
expect(component.exportSettingsForm.get('cccExportType')?.hasValidator(Validators.required)).toBeFalse();
279+
expect(component.exportSettingsForm.get('cccExportGroup')?.hasValidator(Validators.required)).toBeFalse();
280+
expect(component.exportSettingsForm.get('cccExportDate')?.hasValidator(Validators.required)).toBeFalse();
281+
expect(component.exportSettingsForm.get('cccExportType')?.value).toBeNull();
282+
}));
283+
});
284+
285+
describe('CCC Export Type Watchers', () => {
286+
it('should handle cccExportType being changed to Charge Card Transaction', fakeAsync(() => {
287+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
288+
tick();
289+
290+
expect(component.exportSettingsForm.get('chargeCard')?.hasValidator(Validators.required)).toBeTrue();
291+
expect(component.exportSettingsForm.get('cccExportGroup')?.disabled).toBeTrue();
292+
expect(component.exportSettingsForm.get('cccExportGroup')?.value).toBe(ExpenseGroupingFieldOption.EXPENSE_ID);
293+
}));
294+
295+
it('should handle cccExportType being changed to Bill', fakeAsync(() => {
296+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.BILL);
297+
tick();
298+
299+
expect(component.exportSettingsForm.get('creditCardVendor')?.hasValidator(Validators.required)).toBeTrue();
300+
}));
301+
302+
it('should handle cccExportType being changed to Expense Report', fakeAsync(() => {
303+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.EXPENSE_REPORT);
304+
tick();
305+
306+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(EmployeeFieldMapping.EMPLOYEE);
307+
expect(component.exportSettingsForm.get('cccExpensePaymentType')?.hasValidator(Validators.required)).toBeTrue();
308+
}));
309+
310+
it('should handle cccExportType being changed to Journal Entry', fakeAsync(() => {
311+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.JOURNAL_ENTRY);
312+
tick();
313+
314+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.enabled).toBeTrue();
315+
expect(component.exportSettingsForm.get('creditCard')?.hasValidator(Validators.required)).toBeTrue();
316+
}));
317+
});
318+
319+
describe('Custom Watchers', () => {
320+
beforeEach(() => {
321+
brandingConfig.brandId = 'fyle';
322+
});
323+
324+
it('should update reimbursable expense grouping date options when group changes', fakeAsync(() => {
325+
fixture.detectChanges();
326+
component.exportSettingsForm.get('reimbursableExportGroup')?.setValue(ExpenseGroupingFieldOption.CLAIM_NUMBER);
327+
tick();
328+
329+
expect(component.reimbursableExpenseGroupingDateOptions).not.toContain({
330+
label: 'Spend date',
331+
value: ExportDateType.SPENT_AT
332+
});
333+
}));
334+
335+
it('should update CCC expense grouping date options when group changes', fakeAsync(() => {
336+
spyOn<IntacctExportSettingsComponent, any>(component, 'setCCExpenseDateOptions').and.callThrough();
337+
spyOn(IntacctExportSettingModel, 'getExpenseGroupingDateOptions').and.callThrough();
338+
spyOn(ExportSettingModel, 'constructGroupingDateOptions').and.callThrough();
339+
340+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
341+
component.exportSettingsForm.get('cccExportGroup')?.setValue(ExpenseGroupingFieldOption.CLAIM_NUMBER);
342+
343+
tick();
344+
345+
expect(IntacctExportSettingModel.getExpenseGroupingDateOptions).toHaveBeenCalledWith();
346+
expect(ExportSettingModel.constructGroupingDateOptions).toHaveBeenCalledWith(
347+
ExpenseGroupingFieldOption.CLAIM_NUMBER,
348+
IntacctExportSettingModel.getExpenseGroupingDateOptions()
349+
);
350+
expect(component['setCCExpenseDateOptions']).toHaveBeenCalled();
351+
}));
352+
});
353+
354+
describe('Export Selection Validator', () => {
355+
beforeEach(() => {
356+
fixture.detectChanges();
357+
});
358+
359+
it('should invalidate form when neither reimbursable nor credit card expense is selected', () => {
360+
component.exportSettingsForm.get('reimbursableExpense')?.setValue(false);
361+
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);
362+
363+
expect(component.exportSettingsForm.valid).toBeFalse();
364+
});
365+
366+
it('should validate the form when at least one export type is selected', () => {
367+
component.exportSettingsForm.get('reimbursableExpense')?.setValue(true);
368+
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);
369+
370+
expect(component.exportSettingsForm.valid).toBeTrue();
371+
});
372+
});
373+
374+
describe('Destination Options Handling', () => {
375+
beforeEach(() => {
376+
fixture.detectChanges();
377+
});
378+
379+
it('should handle option search for reimbursable expense payment type', fakeAsync(() => {
380+
const searchEvent = {
381+
searchTerm: 'test',
382+
destinationOptionKey: 'EXPENSE_PAYMENT_TYPE'
383+
} as ExportSettingOptionSearch;
384+
385+
mappingService.getPaginatedDestinationAttributes.and.returnValue(
386+
of(mockPaginatedDestinationAttributes.EXPENSE_PAYMENT_TYPE as unknown as PaginatedintacctDestinationAttribute)
387+
);
388+
389+
component.searchOptionsDropdown(searchEvent);
390+
tick(1000);
391+
392+
expect(mappingService.getPaginatedDestinationAttributes).toHaveBeenCalledWith('EXPENSE_PAYMENT_TYPE', 'test');
393+
394+
const isReimbursable = (option: IntacctDestinationAttribute) => (
395+
option.detail ? option.detail.is_reimbursable : true
396+
);
397+
398+
expect(component.destinationOptions.EXPENSE_PAYMENT_TYPE.every(isReimbursable)).toBeTrue();
399+
expect(component.isOptionSearchInProgress).toBeFalse();
400+
}));
401+
402+
it('should handle option search for CCC expense payment type', fakeAsync(() => {
403+
const searchEvent = {
404+
searchTerm: 'test',
405+
destinationOptionKey: 'CCC_EXPENSE_PAYMENT_TYPE'
406+
};
407+
408+
mappingService.getPaginatedDestinationAttributes.and.returnValue(
409+
of(mockPaginatedDestinationAttributes.EXPENSE_PAYMENT_TYPE as unknown as PaginatedintacctDestinationAttribute)
410+
);
411+
412+
component.searchOptionsDropdown(searchEvent as ExportSettingOptionSearch);
413+
tick(1000);
414+
415+
expect(mappingService.getPaginatedDestinationAttributes).toHaveBeenCalledWith('EXPENSE_PAYMENT_TYPE', 'test');
416+
expect(component.destinationOptions.CCC_EXPENSE_PAYMENT_TYPE.every(option => (
417+
option.detail ? !option.detail.is_reimbursable : true
418+
))).toBeTrue();
419+
expect(component.isOptionSearchInProgress).toBeFalse();
420+
}));
421+
});
422+
423+
424+
});
425+
426+
describe('C1 Specific Behavior', () => {
427+
it('should handle setup with c1 branding', () => {
428+
brandingConfig.brandId = 'co';
429+
430+
fixture = TestBed.createComponent(IntacctExportSettingsComponent);
431+
component = fixture.componentInstance;
432+
fixture.detectChanges();
433+
434+
expect(component.exportSettingsForm.get('creditCardExpense')?.value).toBeTrue();
435+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.VENDOR);
436+
expect(component.isMultiLineOption).toBeFalse();
437+
});
438+
439+
afterAll(() => {
440+
brandingConfig.brandId = 'fyle';
441+
});
179442
});
180443

181-
it('should navigate to previous step', () => {
182-
component.navigateToPreviousStep();
183-
expect(router.navigate).toHaveBeenCalledWith(['/integrations/intacct/onboarding/connector']);
444+
describe('Edge Cases', () => {
445+
it('should set the correct CCC expense grouping date options when CCC expense object is unset', () => {
446+
component.exportSettings = {
447+
configurations: {
448+
corporate_credit_card_expenses_object: null
449+
}
450+
} as ExportSettingGet;
451+
spyOn<any>(component, 'setCCExpenseDateOptions');
452+
453+
component['setupCCCExpenseGroupingDateOptions']();
454+
expect(component['setCCExpenseDateOptions']).toHaveBeenCalledOnceWith(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
455+
});
456+
457+
it('should default CCC expense grouping date options to reimbursable grouping date options for non-charge card transactions', () => {
458+
fixture.detectChanges();
459+
460+
component['setCCExpenseDateOptions'](IntacctCorporateCreditCardExpensesObject.BILL);
461+
expect(component.cccExpenseGroupingDateOptions).toEqual(component.reimbursableExpenseGroupingDateOptions);
462+
});
463+
464+
it('should set the correct CCC expense grouping date options when grouping by report', () => {
465+
fixture.detectChanges();
466+
467+
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
468+
component['updateCCCGroupingDateOptions'](ExpenseGroupingFieldOption.CLAIM_NUMBER);
469+
470+
expect(component.cccExpenseGroupingDateOptions).toEqual([
471+
{
472+
label: brandingContent.common.currentDate,
473+
value: ExportDateType.CURRENT_DATE
474+
},
475+
{
476+
label: 'Last Spend Date',
477+
value: ExportDateType.LAST_SPENT_AT
478+
},
479+
{
480+
label: 'Approved Date',
481+
value: ExportDateType.APPROVAL_DATE
482+
},
483+
{
484+
label: 'Card Transaction Post date',
485+
value: ExportDateType.POSTED_AT
486+
}
487+
]);
488+
});
489+
490+
it('should enable the employeeFieldMapping field when at least one export type is Journal Entry', () => {
491+
fixture.detectChanges();
492+
component.exportSettings.configurations.reimbursable_expenses_object = IntacctReimbursableExpensesObject.JOURNAL_ENTRY;
493+
component.exportSettings.configurations.corporate_credit_card_expenses_object = IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION;
494+
495+
component['exportFieldsWatcher']();
496+
497+
expect(component.exportSettingsForm.get('employeeFieldMapping')?.enabled).toBeTrue();
498+
});
184499
});
185500

186501
describe('Watchers', () => {

0 commit comments

Comments
 (0)