Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: intacct export settings watchers #1002

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { provideRouter, Router, RouterModule } from '@angular/router';
import { of, throwError } from 'rxjs';
import { IntacctExportSettingsComponent } from './intacct-export-settings.component';
Expand All @@ -9,10 +9,13 @@
import { IntegrationsToastService } from 'src/app/core/services/common/integrations-toast.service';
import { TrackingService } from 'src/app/core/services/integration/tracking.service';
import { mockExportSettings, mockPaginatedDestinationAttributes } from '../../intacct.fixture';
import { IntacctOnboardingState, Page, ToastSeverity } from 'src/app/core/models/enum/enum.model';
import { ExportSettingOptionSearch } from 'src/app/core/models/common/export-settings.model';
import { EmployeeFieldMapping, ExpenseGroupingFieldOption, ExportDateType, FyleField, IntacctCorporateCreditCardExpensesObject, IntacctOnboardingState, IntacctReimbursableExpensesObject, Page, ToastSeverity } from 'src/app/core/models/enum/enum.model';
import { ExportSettingOptionSearch, ExportSettingModel } from 'src/app/core/models/common/export-settings.model';
import { IntacctDestinationAttribute, PaginatedintacctDestinationAttribute } from 'src/app/core/models/intacct/db/destination-attribute.model';
import { SharedModule } from 'src/app/shared/shared.module';
import { brandingConfig } from 'src/app/branding/branding-config';
import { BrandingConfiguration } from 'src/app/core/models/branding/branding-configuration.model';
import { ExportSettingModel as IntacctExportSettingModel } from 'src/app/core/models/intacct/intacct-configuration/export-settings.model';

describe('IntacctExportSettingsComponent', () => {
let component: IntacctExportSettingsComponent;
Expand Down Expand Up @@ -57,7 +60,7 @@
mappingService.refreshSageIntacctDimensions.and.returnValue(of(null));
mappingService.refreshFyleDimensions.and.returnValue(of(null));

const copy = structuredClone(mockPaginatedDestinationAttributes);
const copy = structuredClone(mockPaginatedDestinationAttributes);
mappingService.getPaginatedDestinationAttributes.and.returnValues(
of(copy.ACCOUNT as unknown as PaginatedintacctDestinationAttribute),
of(copy.EXPENSE_PAYMENT_TYPE as unknown as PaginatedintacctDestinationAttribute),
Expand Down Expand Up @@ -178,4 +181,225 @@
component.navigateToPreviousStep();
expect(router.navigate).toHaveBeenCalledWith(['/integrations/intacct/onboarding/connector']);
});

describe('Watchers', () => {
beforeEach(() => {
fixture.detectChanges();
});

describe('Reimbursable Expense Toggle Watcher', () => {
it('should enable fields on enabling reimbursable expenses', fakeAsync(() => {
component.exportSettingsForm.get('reimbursableExpense')?.setValue(true);
tick();

expect(component.exportSettingsForm.get('reimbursableExportType')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('reimbursableExportGroup')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('reimbursableExportDate')?.hasValidator(Validators.required)).toBeTrue();
}));

it('should disable fields on disabling reimbursable expenses', fakeAsync(() => {
component.exportSettingsForm.get('reimbursableExpense')?.setValue(false);
tick();

expect(component.exportSettingsForm.get('reimbursableExportType')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('reimbursableExportGroup')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('reimbursableExportDate')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('reimbursableExportType')?.value).toBeNull();
}));
});

describe('Reimbursable Export Type Watchers', () => {

it('should handle reimbursableExportType being changed to Journal Entry', fakeAsync(() => {
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.JOURNAL_ENTRY);
tick();

expect(component.exportSettingsForm.get('glAccount')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('employeeFieldMapping')?.enabled).toBeTrue();
}));

it('should handle reimbursableExportType being changed to Expense Report', fakeAsync(() => {
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.EXPENSE_REPORT);
tick();

expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.EMPLOYEE);
expect(component.exportSettingsForm.get('employeeFieldMapping')?.disabled).toBeTrue();
}));

it('should handle reimbursableExportType being changed to Bill', fakeAsync(() => {
component.exportSettingsForm.get('reimbursableExportType')?.setValue(IntacctReimbursableExpensesObject.BILL);
tick();

expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.VENDOR);
expect(component.exportSettingsForm.get('employeeFieldMapping')?.disabled).toBeTrue();
}));
});

describe('Credit Card Expense Toggle Watcher', () => {
it('should enable fields on enabling CCC expenses', fakeAsync(() => {
component.exportSettingsForm.get('creditCardExpense')?.setValue(true);
tick();

expect(component.exportSettingsForm.get('cccExportType')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('cccExportGroup')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('cccExportDate')?.hasValidator(Validators.required)).toBeTrue();
}));

it('should disable fields on disabling CCC expenses', fakeAsync(() => {
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);
tick();

expect(component.exportSettingsForm.get('cccExportType')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('cccExportGroup')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('cccExportDate')?.hasValidator(Validators.required)).toBeFalse();
expect(component.exportSettingsForm.get('cccExportType')?.value).toBeNull();
}));
});

describe('CCC Export Type Watchers', () => {
it('should handle cccExportType being changed to Charge Card Transaction', fakeAsync(() => {
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
tick();

expect(component.exportSettingsForm.get('chargeCard')?.hasValidator(Validators.required)).toBeTrue();
expect(component.exportSettingsForm.get('cccExportGroup')?.disabled).toBeTrue();
expect(component.exportSettingsForm.get('cccExportGroup')?.value).toBe(ExpenseGroupingFieldOption.EXPENSE_ID);
}));

it('should handle cccExportType being changed to Bill', fakeAsync(() => {
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.BILL);
tick();

expect(component.exportSettingsForm.get('creditCardVendor')?.hasValidator(Validators.required)).toBeTrue();
}));

it('should handle cccExportType being changed to Expense Report', fakeAsync(() => {
component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.EXPENSE_REPORT);
tick();

expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(EmployeeFieldMapping.EMPLOYEE);
expect(component.exportSettingsForm.get('cccExpensePaymentType')?.hasValidator(Validators.required)).toBeTrue();
}));
});

describe('Custom Watchers', () => {
beforeEach(() => {
brandingConfig.brandId = 'fyle';
});

it('should update reimbursable expense grouping date options when group changes', fakeAsync(() => {
fixture.detectChanges();
component.exportSettingsForm.get('reimbursableExportGroup')?.setValue(ExpenseGroupingFieldOption.CLAIM_NUMBER);
tick();

expect(component.reimbursableExpenseGroupingDateOptions).not.toContain({
label: 'Spend date',
value: ExportDateType.SPENT_AT
});
}));

it('should update CCC expense grouping date options when group changes', fakeAsync(() => {
spyOn<IntacctExportSettingsComponent, any>(component, 'setCCExpenseDateOptions').and.callThrough();
spyOn(IntacctExportSettingModel, 'getExpenseGroupingDateOptions').and.callThrough();
spyOn(ExportSettingModel, 'constructGroupingDateOptions').and.callThrough();

component.exportSettingsForm.get('cccExportType')?.setValue(IntacctCorporateCreditCardExpensesObject.CHARGE_CARD_TRANSACTION);
component.exportSettingsForm.get('cccExportGroup')?.setValue(ExpenseGroupingFieldOption.CLAIM_NUMBER);

tick();

expect(IntacctExportSettingModel.getExpenseGroupingDateOptions).toHaveBeenCalledWith();
expect(ExportSettingModel.constructGroupingDateOptions).toHaveBeenCalledWith(
ExpenseGroupingFieldOption.CLAIM_NUMBER,
IntacctExportSettingModel.getExpenseGroupingDateOptions()
);
expect(component['setCCExpenseDateOptions']).toHaveBeenCalled();

Check failure on line 316 in src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.spec.ts

View workflow job for this annotation

GitHub Actions / lint

["setCCExpenseDateOptions"] is better written in dot notation
JustARatherRidiculouslyLongUsername marked this conversation as resolved.
Show resolved Hide resolved
}));
});

describe('Export Selection Validator', () => {
beforeEach(() => {
fixture.detectChanges();
});

it('should invalidate form when neither reimbursable nor credit card expense is selected', () => {
component.exportSettingsForm.get('reimbursableExpense')?.setValue(false);
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);

expect(component.exportSettingsForm.valid).toBeFalse();
});

it('should validate the form when at least one export type is selected', () => {
component.exportSettingsForm.get('reimbursableExpense')?.setValue(true);
component.exportSettingsForm.get('creditCardExpense')?.setValue(false);

expect(component.exportSettingsForm.valid).toBeTrue();
});
});

describe('Destination Options Handling', () => {
beforeEach(() => {
fixture.detectChanges();
});

it('should handle option search for reimbursable expense payment type', fakeAsync(() => {
const searchEvent = {
searchTerm: 'test',
destinationOptionKey: 'EXPENSE_PAYMENT_TYPE'
} as ExportSettingOptionSearch;

mappingService.getPaginatedDestinationAttributes.and.returnValue(
of(mockPaginatedDestinationAttributes.EXPENSE_PAYMENT_TYPE as unknown as PaginatedintacctDestinationAttribute)
);

component.searchOptionsDropdown(searchEvent);
tick(1000);

expect(mappingService.getPaginatedDestinationAttributes).toHaveBeenCalledWith('EXPENSE_PAYMENT_TYPE', 'test');
expect(component.destinationOptions.EXPENSE_PAYMENT_TYPE.every(option => (option.detail ? option.detail.is_reimbursable : true))).toBeTrue();

Check failure on line 359 in src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Too many nested callbacks (5). Maximum allowed is 4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Reduce nested callbacks to enhance readability

At line 359, there's a nesting depth of 5 callbacks, which exceeds the maximum recommended depth of 4. This can make the test harder to read and maintain.

Consider refactoring the test to reduce the nesting level. You might extract some of the inner functions or use async/await if applicable.

🧰 Tools
🪛 GitHub Check: lint

[failure] 359-359:
Too many nested callbacks (5). Maximum allowed is 4

expect(component.isOptionSearchInProgress).toBeFalse();
}));

it('should handle option search for CCC expense payment type', fakeAsync(() => {
const searchEvent = {
searchTerm: 'test',
destinationOptionKey: 'CCC_EXPENSE_PAYMENT_TYPE'
};

mappingService.getPaginatedDestinationAttributes.and.returnValue(
of(mockPaginatedDestinationAttributes.EXPENSE_PAYMENT_TYPE as unknown as PaginatedintacctDestinationAttribute)
);

component.searchOptionsDropdown(searchEvent as ExportSettingOptionSearch);
tick(1000);

expect(mappingService.getPaginatedDestinationAttributes).toHaveBeenCalledWith('EXPENSE_PAYMENT_TYPE', 'test');
expect(component.destinationOptions.CCC_EXPENSE_PAYMENT_TYPE.every(option => (

Check failure on line 377 in src/app/integrations/intacct/intacct-shared/intacct-export-settings/intacct-export-settings.component.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Too many nested callbacks (5). Maximum allowed is 4
JustARatherRidiculouslyLongUsername marked this conversation as resolved.
Show resolved Hide resolved
option.detail ? !option.detail.is_reimbursable : true
))).toBeTrue();
expect(component.isOptionSearchInProgress).toBeFalse();
}));
});


});


describe('C1 Specific Behavior', () => {
it('should handle setup with c1 branding', () => {
brandingConfig.brandId = 'co';

fixture = TestBed.createComponent(IntacctExportSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();

expect(component.exportSettingsForm.get('creditCardExpense')?.value).toBeTrue();
expect(component.exportSettingsForm.get('employeeFieldMapping')?.value).toBe(FyleField.VENDOR);
expect(component.isMultiLineOption).toBeFalse();
});

afterAll(() => {
brandingConfig.brandId = 'fyle';
});
});
JustARatherRidiculouslyLongUsername marked this conversation as resolved.
Show resolved Hide resolved
});
Loading