import { VettingRequestStatusId } from '@core/typings/application.typing'; import { BucketSearchAPIResponse } from '@yourcause/common'; import { MockModule } from '@yourcause/common/testing'; import { BeforeEach, Spec, TestCase } from '@yourcause/test-decorators'; import { DescribeAngularService } from '@yourcause/test-decorators/angular'; import { expect, spy } from 'chai'; import { ApplicationEligibilityService } from './application-eligibility.service'; import { ApplicationEligibility } from './application-eligibility.typing'; @DescribeAngularService(ApplicationEligibilityService, { imports: [MockModule] }) export class ApplicationEligibilityServiceSpec implements Spec { payloadWithoutCharityBucket: ApplicationEligibility.ProgramUpdateEligibilityPayload = { applicationId: 1, grantProgramId: 2, grantProgramCycleId: 3, orgIdentification: 'string', charityBucketId: null, clientId: 4, latestVettingRequestStatusForOrg: null }; payloadWithNoOrg: ApplicationEligibility.ProgramUpdateEligibilityPayload = { applicationId: 1, grantProgramId: 2, grantProgramCycleId: 3, orgIdentification: '', charityBucketId: null, clientId: 4, latestVettingRequestStatusForOrg: null }; payloadWithVetting: ApplicationEligibility.ProgramUpdateEligibilityPayload = { applicationId: 1, grantProgramId: 2, grantProgramCycleId: 3, orgIdentification: 'string', charityBucketId: null, clientId: 4, latestVettingRequestStatusForOrg: VettingRequestStatusId.REQUEST_SUBMITTED }; payloadWithCharityBucket: ApplicationEligibility.ProgramUpdateEligibilityPayload = { applicationId: 1, grantProgramId: 2, grantProgramCycleId: 3, orgIdentification: '123', charityBucketId: 'string', clientId: 4, latestVettingRequestStatusForOrg: null }; @BeforeEach() reset () { spy.restore(); } @TestCase('should be able to check if cycle update can proceed - eligible w/out charity bucket') async testCheckingForCycleUpdateValidityEligibleNoBucket (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationEligible; }); const payload = this.payloadWithoutCharityBucket; const result = await service.checkIfCycleUpdateCanProceed(payload); expect(result).to.equal(null); expect(service['organizationEligibilityCheck']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - eligible with charity bucket') async testCheckingForCycleUpdateValidityEligibleWithBucket (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationEligible; }); spy.on(service['nonprofitService'], 'searchNonprofitsWithinBucket', async () => { return { data: [{ registrationNumber: '123' }] } as BucketSearchAPIResponse; }); const payload = this.payloadWithCharityBucket; const result = await service.checkIfCycleUpdateCanProceed(payload); expect(result).to.equal(null); expect(service['organizationEligibilityCheck']).to.have.been.called(); expect(service['nonprofitService']['searchNonprofitsWithinBucket']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - Org not in NPP') async testCheckingForCycleUpdateValidityOrgNotInNPP (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationNotActiveInNpp; }); const payload = this.payloadWithoutCharityBucket; const result = await service.checkIfCycleUpdateCanProceed(payload); expect(result).to.equal(`This application cannot be copied or moved. The organization linked to this application is no longer active in YourCause + Blackbaud's Global Good Network and cannot accept applications.`); expect(service['organizationEligibilityCheck']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - Not eligible for giving') async testCheckingForCycleUpdateValidityOrgNotEligibleForGiving (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationNotEligibleForGiving; }); const payload = this.payloadWithoutCharityBucket; const result = await service.checkIfCycleUpdateCanProceed(payload); expect(result).to.equal('This application cannot be copied or moved. The organization linked to this application is not currently eligible for giving.'); expect(service['organizationEligibilityCheck']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - Not valid for budget') async testCheckingForCycleUpdateValidityOrgNotValidForBudget (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationNotValidForGrantProgramBudget; }); const payload = this.payloadWithoutCharityBucket; const result = await service.checkIfCycleUpdateCanProceed(payload); expect(result).to.equal('This application cannot be copied or moved. The organization linked to this application is not valid for the selected budget at this time.'); expect(service['organizationEligibilityCheck']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - Org in charity bucket') async testCheckingForCycleUpdateValidityOrgInCharityBucket (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationEligible; }); spy.on(service['nonprofitService'], 'searchNonprofitsWithinBucket', async () => { return { data: [{ registrationNumber: '123' }] } as BucketSearchAPIResponse; }); const result = await service.checkIfCycleUpdateCanProceed(this.payloadWithCharityBucket); expect(result).to.equal(null); expect(service['organizationEligibilityCheck']).to.have.been.called(); expect(service['nonprofitService']['searchNonprofitsWithinBucket']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - Org not in charity bucket') async testCheckingForCycleUpdateValidityOrgNotInCharityBucket (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationEligible; }); spy.on(service['nonprofitService'], 'searchNonprofitsWithinBucket', async () => { return { data: [{ // org not found registrationNumber: '234' }] } as BucketSearchAPIResponse; }); const result = await service.checkIfCycleUpdateCanProceed(this.payloadWithCharityBucket); expect(result).to.equal('This application cannot be copied or moved. This organization is no longer included in the preferred charity list for this program.'); expect(service['organizationEligibilityCheck']).to.have.been.called(); expect(service['nonprofitService']['searchNonprofitsWithinBucket']).to.have.been.called(); } @TestCase('should be able to check if cycle update can proceed - with vetting') async checkIfCycleUpdateCanProceedWithVetting (service: ApplicationEligibilityService) { spy.on(service, 'organizationEligibilityCheck', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationNotEligibleForGiving; }); const message = await service.checkIfCycleUpdateCanProceed(this.payloadWithVetting); const canProceed = !message; // Can proceed bc vetting request exists for this already expect(canProceed).to.be.true; } @TestCase('should be able to check if cycle update can proceed - no org') async checkIfCycleUpdateCanProceedNoOrg (service: ApplicationEligibilityService) { const message = await service.checkIfCycleUpdateCanProceed(this.payloadWithNoOrg); const canProceed = !message; // no org so we allow it expect(canProceed).to.be.true; } @TestCase('should be able to show eligibility info') async testShowingEligibilityInfo (service: ApplicationEligibilityService) { const modalSpy = spy.on(service['modalFactory'], 'open', () => {}); await service.showErrorCopyingApplication('hello', 'test'); expect(modalSpy).to.be.called(); } @TestCase('should be able to check if cycle update can proceed - for applicant') async checkIfCycleUpdateCanProceedForApplicant (service: ApplicationEligibilityService) { spy.on(service, 'checkOrganizationEligibilityStatus', async () => { return ApplicationEligibility.OrganizationEligibilityCheckResponse.OrganizationNotEligibleForGiving; }); const message = await service.checkIfCycleUpdateCanProceed(this.payloadWithVetting, true); const canProceed = !message; // Can proceed bc vetting request exists for this already expect(canProceed).to.be.true; } }