import { ReactiveFormsModule } from '@angular/forms'; import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { CreditApplicationDaoService } from './credit-application-dao.service'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { CreditApplicationService } from './credit-application.service'; import { configureTestSuite } from 'ng-bullet'; import { LoanType, LoanTypeSet } from './loan-type'; import { LoanTitle } from './loan-type/loan-title'; import { CreditApplicationValues } from './credit-application-values'; import { of } from 'rxjs'; import { apiTranslatorPreTranslate } from '../testing/shared-mocks/api-translator-pre-post'; import { EthnicityInformation } from '../loan-wizard/field-groups/hmda/ethnicity-information'; import { GenderInformation } from '../loan-wizard/field-groups/hmda/gender-information'; import { RaceInformation } from '../loan-wizard/field-groups/hmda/race-information'; import { BorrowerType } from './borrower-type'; import { EmploymentInformation } from '../loan-wizard/field-groups/employment/employment-information'; import { IncomeInformation } from '../loan-wizard/field-groups/employment/income-information'; import { AlimonyInformation } from '../loan-wizard/field-groups/employment/alimony-information'; import { CreditApplication, SpouseType, TattleIndividual } from './credit-application'; import { AddressInformation } from '../loan-wizard/field-groups/address/address-information'; import { ResidenceInformation } from '../loan-wizard/field-groups/address/residence-information'; import { PersonalInformation } from '../loan-wizard/field-groups/personal/personal-information'; import { IsFullInfoSpouse } from '../loan-wizard/field-groups/spouse-type/is-full-info-spouse-information'; import { PhoneTypeEnum, PropertyTypeEnum } from './api-models/api-enums'; import { MembershipInformation } from '../loan-wizard/field-groups/member/membership-information'; import { primaryAndCoApplicationAddressSameApplication } from '../testing/shared-mocks/broken-applications'; import { ConfigurationService } from './configuration/configuration.service'; import { UiSettings } from './configuration/ui-settings'; import { DefaultSettings } from './configuration/default-settings.mock'; describe('CreditApplicationService', () => { let creditApplicationService: CreditApplicationService; let creditApplicationDaoService: CreditApplicationDaoService; let httpMock: HttpTestingController; configureTestSuite(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, NgbModule.forRoot(), ReactiveFormsModule, HttpClientTestingModule ], schemas: [ NO_ERRORS_SCHEMA ], declarations: [], providers: [ CreditApplicationDaoService ] }); }); beforeEach(() => { creditApplicationService = TestBed.get(CreditApplicationService); creditApplicationDaoService = TestBed.get(CreditApplicationDaoService); httpMock = TestBed.get(HttpTestingController); creditApplicationService.setLoanType(LoanTypeSet[0].id); }); it('should create', () => { expect(creditApplicationService).toBeTruthy(); }); it('should save term of loan for equity member', () => { creditApplicationService.setLoanType(LoanTypeSet[4].id); spyOn(creditApplicationService, 'getFullCreditApplication').and.callThrough(); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const testStub = { 'applicantType': CreditApplicationValues.CHOICE_NO, 'limitRequested': '1000', 'termOfLoan': 120, 'maritalStatus': CreditApplicationValues.CHOICE_NO, 'clientMessage': 'test message' }; creditApplicationService.saveEquityMemberInformation(testStub); const creditApplication = creditApplicationService.getFullCreditApplication(); expect(creditApplicationService.getFullCreditApplication).toHaveBeenCalled(); expect(creditApplication.termOfLoan).toBeGreaterThanOrEqual(120); expect(creditApplication.clientMessage).toEqual(testStub.clientMessage); }); it('should save HMDA information', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const ethnicity: EthnicityInformation = { cuban: true, otherHispanicLatinoText: 'San Lorenzoan' }; const sex: GenderInformation = { male: true, female: true }; const race: RaceInformation = { filipino: true, americanIndianAlaskaNativeText: 'False Karass' }; const hmda = { ethnicityInformation: ethnicity, genderInformation: sex, raceInformation: race }; creditApplicationService.saveHMDAInformation(hmda, BorrowerType.CO_BORROWER); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[1].ethnicityInformation).toEqual(ethnicity); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[1].genderInformation).toEqual(sex); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[1].raceInformation).toEqual(race); }); it('should set loantype if existing credit application has different loantype', () => { const result = creditApplicationService.shouldSetupCreditApplication(LoanTypeSet[1]); expect(result).toBe(true); }); it('should NOT set loantype if existing credit application has same loantype', () => { const result = creditApplicationService.shouldSetupCreditApplication(LoanTypeSet[0]); expect(result).toBe(false); }); it('should save and process employment/income information, only once', () => { const testEmploymentInformation: Array = [{ employerName: 'emp1', employmentType: '3', jobTitle: 'senior qa', salary: 5000, schedule: 'Hourly', employmentLengthYears: 5, employmentLengthMonths: 0, workPhoneNumber: '' }]; const testIncomeInformation: Array = [{ incomeName: 'Pension', amount: 4000 }, { incomeName: 'Gambling', amount: 10000 }, { incomeName: 'Check from parents', amount: 20000 }]; const testAlimonyInformaiton: AlimonyInformation = { childSupportAmount: 3000 }; spyOn(creditApplicationService, 'saveEmploymentInformation').and.callThrough(); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); creditApplicationService.saveAllEmploymentInformation( testEmploymentInformation, testIncomeInformation, testAlimonyInformaiton, BorrowerType.PRIMARY_BORROWER); // Bug #840, Make sure calling twice doesn't ADD creditApplicationService.saveAllEmploymentInformation( testEmploymentInformation, testIncomeInformation, testAlimonyInformaiton, BorrowerType.PRIMARY_BORROWER); const employmentInfo = creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].employmentInformation; const otherIncome = creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].otherIncomeInformation; const alimony = creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].alimonyInformation; expect(creditApplicationService.getFullCreditApplication().page.hasEmployment).toBe(true); expect(employmentInfo).toBeTruthy(); expect(employmentInfo.length).toEqual(1); // Called twice, should only be one. expect(employmentInfo[0].jobTitle).toEqual('senior qa'); expect(employmentInfo[0].employerName).toEqual('emp1'); expect(employmentInfo[0].salary).toEqual(5000); expect(otherIncome[0].amount).toEqual(4000); expect(otherIncome[0].incomeName).toBe('Pension'); expect(otherIncome[1].amount).toEqual(10000); expect(otherIncome[1].incomeName).toBe('Gambling'); expect(otherIncome[2].amount).toEqual(20000); expect(otherIncome[2].incomeName).toBe('Check from parents'); expect(alimony.childSupportAmount).toEqual(3000); }); // tslint:disable-next-line:max-line-length it('should remove spouseType from a fullInfoBorrower who has changed state of residence such that spouse information is not required', () => { const marriedPrimaryApplication: CreditApplication = apiTranslatorPreTranslate; marriedPrimaryApplication.fullInfoBorrowers[0].fullInfoSpouse = apiTranslatorPreTranslate.fullInfoBorrowers[1]; marriedPrimaryApplication.fullInfoBorrowers[0].spouseType = SpouseType.FullInfoSpouse; spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); const addressInformation: AddressInformation = marriedPrimaryApplication.fullInfoBorrowers[0].addressInformation; addressInformation.state = 'IL'; creditApplicationService.saveAllAddressInformation(BorrowerType.PRIMARY_BORROWER, addressInformation); expect(creditApplicationService.getFullCreditApplication().page.hasAddress).toBe(true); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].spouseType).toBeFalsy(); // This is an 'after' hook addressInformation.state = CreditApplicationValues.STATE_REQUIRED_COAPP; creditApplicationService.saveAllAddressInformation(BorrowerType.PRIMARY_BORROWER, addressInformation); }); it('should save address and residence history', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const addressInformation: AddressInformation = apiTranslatorPreTranslate.fullInfoBorrowers[0].addressInformation; const residenceInformation: ResidenceInformation = apiTranslatorPreTranslate.fullInfoBorrowers[0].residenceInformation; const priorAddress: Array = [apiTranslatorPreTranslate.fullInfoBorrowers[1].addressInformation]; const priorResidence: Array = [apiTranslatorPreTranslate.fullInfoBorrowers[1].residenceInformation]; creditApplicationService.saveAddressAndResidenceInformation({ addressInfo: addressInformation, residenceInfo: residenceInformation, priorAddressInfo: priorAddress, priorResidenceInfo: priorResidence }, BorrowerType.CO_BORROWER); const result = creditApplicationService.getFullCreditApplication(); expect(result.fullInfoBorrowers[1].addressInformation).toEqual(addressInformation); expect(result.fullInfoBorrowers[1].residenceInformation).toEqual(residenceInformation); expect(result.fullInfoBorrowers[1].priorAddressInformation).toEqual(priorAddress); expect(result.fullInfoBorrowers[1].priorResidenceInformation).toEqual(priorResidence); }); it('should save mailing address when it differs from current address', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const PRIMARY = 0; const mailingAddress = { addressLine1: '200 S Wacker', addressLine2: 'Dr # 3600', zipcode: '60606', city: 'Chicago', state: 'IL' }; apiTranslatorPreTranslate.fullInfoBorrowers[PRIMARY].residenceInformation.isMailingAddressSame = false; apiTranslatorPreTranslate.fullInfoBorrowers[PRIMARY].mailingAddressInformation = mailingAddress; const addressInformation: AddressInformation = apiTranslatorPreTranslate.fullInfoBorrowers[PRIMARY].addressInformation; const residenceInformation: ResidenceInformation = apiTranslatorPreTranslate.fullInfoBorrowers[PRIMARY].residenceInformation; const mailingInformation: AddressInformation = apiTranslatorPreTranslate.fullInfoBorrowers[PRIMARY].mailingAddressInformation; creditApplicationService.saveAddressAndResidenceInformation({ addressInfo: addressInformation, residenceInfo: residenceInformation, mailingInfo: mailingInformation }, BorrowerType.PRIMARY_BORROWER); const result = creditApplicationService.getFullCreditApplication(); expect(result.fullInfoBorrowers[PRIMARY].addressInformation).toEqual(addressInformation); expect(result.fullInfoBorrowers[PRIMARY].mailingAddressInformation).toEqual(mailingInformation); expect(result.fullInfoBorrowers[PRIMARY].residenceInformation).toEqual(residenceInformation); }); it('should determine if there is enough data to save ', () => { const application = creditApplicationService.getFullCreditApplication(); application.fullInfoBorrowers[0].personalInformation.firstName = 'Bob'; application.fullInfoBorrowers[0].addressInformation.addressLine1 = '1235 Maple Ave'; const result = creditApplicationService.hasMinimumSaveInformation(); expect(result).toBe(true); }); it('should nullify previously entered spouse information when an applicant changes their marital status from YES to NO', () => { const marriedPrimaryApplication: CreditApplication = apiTranslatorPreTranslate; marriedPrimaryApplication.fullInfoBorrowers[0].fullInfoSpouse = apiTranslatorPreTranslate.fullInfoBorrowers[1]; marriedPrimaryApplication.fullInfoBorrowers[0].spouseType = SpouseType.FullInfoSpouse; spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].spouseType).toBe(SpouseType.FullInfoSpouse); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].fullInfoSpouse).toBeTruthy(); const personalInfo: PersonalInformation = { firstName: 'Someone', lastName: 'who changed their mind', dateOfBirth: 'about being married', primaryPhoneNumber: 'after already', primaryPhoneNumberType: PhoneTypeEnum.Mobile, socialSecurityNumber: 'submitting a bunch of information about an imaginary spouse', email: 'notMarried@forever_alone.com', maritalStatus: CreditApplicationValues.CHOICE_NO }; creditApplicationService.savePersonalInformation(personalInfo, BorrowerType.PRIMARY_BORROWER); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].spouseType).toBeFalsy(); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].fullInfoSpouse).toBeFalsy(); }); it('should nullify previously entered spouse information when an applicant changes their marital status from NO to co-applicant', () => { const marriedPrimaryApplication: CreditApplication = apiTranslatorPreTranslate; marriedPrimaryApplication.fullInfoBorrowers[0].fullInfoSpouse = apiTranslatorPreTranslate.fullInfoBorrowers[1]; marriedPrimaryApplication.fullInfoBorrowers[0].spouseType = SpouseType.FullInfoSpouse; spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].spouseType).toBe(SpouseType.FullInfoSpouse); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].fullInfoSpouse).toBeTruthy(); const personalInfo: PersonalInformation = { firstName: 'Someone', lastName: 'who changed their mind', dateOfBirth: 'about being married', primaryPhoneNumber: 'after already', primaryPhoneNumberType: PhoneTypeEnum.Mobile, socialSecurityNumber: 'submitting a bunch of information about an imaginary spouse', email: 'notMarried@forever_alone.com', maritalStatus: CreditApplicationValues.CHOICE_YES, coApplicantSpouse: CreditApplicationValues.CHOICE_YES }; creditApplicationService.savePersonalInformation(personalInfo, BorrowerType.PRIMARY_BORROWER); expect(creditApplicationService.getFullCreditApplication().page.hasPersonal).toBe(true); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].spouseType).toBeFalsy(); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].fullInfoSpouse).toBeFalsy(); }); it('should save tattle spouse information', () => { const marriedPrimaryApplication: CreditApplication = apiTranslatorPreTranslate; const tattleSpouse: TattleIndividual = { personalInformation: { firstName: 'Tattly', lastName: 'McSpouseface' } }; spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); creditApplicationService.saveTattleSpouseInformation(tattleSpouse, BorrowerType.PRIMARY_BORROWER_SPOUSE); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].tattleSpouse).toBeTruthy(); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].tattleSpouse).toEqual(tattleSpouse); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[0].fullInfoSpouse).toBeFalsy(); }); it('should set spouseType information', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const spouseType: IsFullInfoSpouse = { isFullInfoSpouse: CreditApplicationValues.CHOICE_YES }; creditApplicationService.saveSpouseTypeInformation(spouseType, BorrowerType.PRIMARY_BORROWER); expect(creditApplicationService.getSpouseType(BorrowerType.PRIMARY_BORROWER)).toBe(SpouseType.FullInfoSpouse); expect(creditApplicationService.getIndividualGroupValues('addressInformation', BorrowerType.PRIMARY_BORROWER_SPOUSE)).toBeTruthy(); }); it('should save an application ', () => { const application = creditApplicationService.getFullCreditApplication(); application.fullInfoBorrowers[0].personalInformation.firstName = 'Bob'; application.fullInfoBorrowers[0].addressInformation.addressLine1 = '1235 Maple Ave'; spyOn(creditApplicationDaoService, 'saveApplication').and.returnValue(of(true)); creditApplicationService.saveCreditApplication(); expect(creditApplicationDaoService.saveApplication).toHaveBeenCalled(); }); it('should gather member information from the application', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); const expectedReturn: MembershipInformation = { limitRequested: apiTranslatorPreTranslate.limitRequested, applicantType: apiTranslatorPreTranslate.hasCoApplicant, livesInMaritalPropertyState: CreditApplicationValues.CHOICE_YES, confirmJointCredit: apiTranslatorPreTranslate.confirmJointCredit, coApplicantLivesInMaritalPropertyState: CreditApplicationValues.CHOICE_YES, purposeOfLoanSignature: apiTranslatorPreTranslate.purposeOfLoanSignature, agreementRequirement: undefined }; expect(creditApplicationService.getMemberInformation()).toEqual(expectedReturn); }); it('should determine that an applicant (or coapplicant) is married', () => { spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(apiTranslatorPreTranslate); expect(creditApplicationService.isMarriedApplicant()).toBe(true); spyOn(creditApplicationService, 'getIndividualByBorrowerType').and.returnValue({ personalInformation: { maritalStatus: CreditApplicationValues.CHOICE_NO } }); expect(creditApplicationService.isMarriedApplicant()).toBe(false); }); it('should handle situations where applications change their mind about co-applicant spouse', () => { const marriedPrimaryApplication: any = primaryAndCoApplicationAddressSameApplication; spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); expect(creditApplicationService.getFullCreditApplication() .fullInfoBorrowers[1].hasSameAddressAsSpouse).toEqual(CreditApplicationValues.CHOICE_YES); const personalInfo: PersonalInformation = Object.assign({}, primaryAndCoApplicationAddressSameApplication.fullInfoBorrowers[0].personalInformation); personalInfo.coApplicantSpouse = CreditApplicationValues.CHOICE_NO; creditApplicationService.savePersonalInformation(personalInfo, BorrowerType.PRIMARY_BORROWER); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers[1].hasSameAddressAsSpouse).toBeNull(); }); it('should clear stale co-applicant spouse state when switching back to a single applicant', () => { const marriedPrimaryApplication: any = JSON.parse(JSON.stringify(primaryAndCoApplicationAddressSameApplication)); spyOn(creditApplicationService, 'getFullCreditApplication').and.returnValue(marriedPrimaryApplication); spyOn(creditApplicationService, 'saveCreditApplication').and.returnValue(of(true)); const memberInfo: MembershipInformation = { limitRequested: marriedPrimaryApplication.limitRequested, applicantType: CreditApplicationValues.CHOICE_NO, livesInMaritalPropertyState: CreditApplicationValues.CHOICE_YES, confirmJointCredit: null, coApplicantLivesInMaritalPropertyState: null, purposeOfLoanSignature: null, agreementRequirement: undefined }; creditApplicationService.saveMemberInformation(memberInfo); expect(creditApplicationService.getFullCreditApplication().fullInfoBorrowers.length).toBe(1); expect(creditApplicationService.getFullCreditApplication().hasCoApplicantSpouse).toBeNull(); expect(creditApplicationService.getFullCreditApplication().coApplicantLivesInMaritalPropertyState).toBeNull(); expect(creditApplicationService.getFullCreditApplication().confirmJointCredit).toBeNull(); }); it('should set Estimated Value & Property Type defaults on HELOC Rate Mod loan type', () => { const loanConfigurationService = TestBed.get(ConfigurationService); loanConfigurationService.saveConfiguration(DefaultSettings as UiSettings); const helocRateMod = LoanTypeSet.find((loanType) => loanType.title === LoanTitle.HELOC_RateMod); if (helocRateMod) { creditApplicationService.setLoanType(helocRateMod.id); } else { creditApplicationService.setLoanType(LoanTypeSet[0].id); const application = creditApplicationService.getFullCreditApplication(); application.loanType = { ...application.loanType, title: LoanTitle.HELOC_RateMod }; } creditApplicationService.saveCollateralInformation( { "collateralInformation": { "isAddressSame": "yes", "estimatedValue": 100000, "propertyType": "Duplex", "isBeingImproved": "false", "isListedForSale": "yes", "occupancyType": "OwnerOccupied", "hasFirstMortgage": "true", "hasSecondMortgage": "false", "payoffFirstMortgage": "false", "addressLine1": "Fake Street 123", "addressLine2": "", "zipcode": "66666", "city": "Test Town", "state": "WI", "residenceStatus": "0", "residenceTimeYears": "2", "residenceTimeMonths": "0", "residenceMonthlyPayment": "2000", "isMailingAddressSame": true } } ); const creditApplication = creditApplicationService.getFullCreditApplication(); expect(creditApplication.loanType.title).toEqual(LoanTitle.HELOC_RateMod); expect(creditApplication.collateralInformation.estimatedValue).toEqual(250000); expect(creditApplication.collateralInformation.propertyType).toEqual(PropertyTypeEnum[PropertyTypeEnum.SingleFamily]); }); });