import { Injectable } from '@angular/core'; import { GCMockModule } from '@core/mocks/gc-module.mock'; import { ViewTranslationsBlank } from '@core/typings/translation.typing'; import { InKindAwardedItemApi } from '@features/in-kind/in-kind.typing'; import { BeforeEach, Spec, TestCase } from '@yourcause/test-decorators'; import { DescribeAngularService } from '@yourcause/test-decorators/angular'; import { expect } from 'chai'; import { PaginationOptions } from 'frontend-common/src/models/grid.model'; import { ApplicationActivityForUi, ApplicationActivityFromApi, GrantActivityChange, GrantActivityChangeFieldType, GrantActivityTypes, PreviousToCurrentActivityEnum } from './activity.typing'; import { ApplicationActivityService } from './application-activity.service'; @Injectable({ providedIn: 'root' }) @DescribeAngularService(ApplicationActivityService, { imports: [ GCMockModule ] }) export class ApplicationActivityServiceSpec implements Spec { appId = 1354; previousAmtRequested = 500; newAmountRequested = 600; currentInKindItems: InKindAwardedItemApi[] = [{ itemIdentification: 'computer123', count: 2, value: 150.0 }, { itemIdentification: 'conf', count: 1, value: 100.0 }]; grantActivityChangeForAmountRequested: GrantActivityChange[] = [{ previous: this.previousAmtRequested + '', current: this.newAmountRequested + '', previousRecommendedFundingAmount: '', currentRecommendedFundingAmount: '', fieldType: GrantActivityChangeFieldType.AmountRequestedUpdated, currentInKindItems: [], previousInKindItems: [], canceledReason: null, updatedBudgetId: null, updatedFundingSourceId: null, previousBudgetId: null, previousFundingSourceId: null, paymentId: null }]; grantActivityChangeForInKind: GrantActivityChange[] = [{ previous: '', current: '400', previousRecommendedFundingAmount: '', currentRecommendedFundingAmount: '', fieldType: null, currentInKindItems: this.currentInKindItems, previousInKindItems: [], canceledReason: null, updatedBudgetId: null, updatedFundingSourceId: null, previousBudgetId: null, previousFundingSourceId: null, paymentId: null }]; amountRequestedUpdateActivity: ApplicationActivityFromApi = { applicationId: this.appId, applicantName: 'Test', formId: 13464, formName: 'Request Form', activityTypeId: GrantActivityTypes.AmountRequestedUpdated, amount: this.newAmountRequested, grantActivityChangeJson: JSON.stringify(this.grantActivityChangeForAmountRequested), id: 5, createdByName: '', createdByProfileImageUrl: '', grantProgramName: 'New Form.io Program', organizationName: 'CHARLESTON PRIDE INC', workFlowLevelName: 'Review', grantProgramCycleName: 'Cycle 1', previousWorkFlowLevelName: null, previousGrantProgramCycleName: null, activityTypeName: null, comment: 'comment!', createdDate: '2022-01-26T19:32:03.4556386Z', isMasked: false, canViewMaskedApplicantInfo: true, isNomination: false, amountRequested: 5045.0000, inKindAmountRequested: 400.0000, createdImpersonatedByName: 'Tina Conway', assignedBudgetName: null, assignedFundingSourceName: null, applicantFullNameForActivity: null, grantManagerFullNameForActivity: null, formDueDate: null, applicantPermissions: null, workflowLevelAutomationRuleName: null, fileName: null, communicationSubject: null, previousOrganizationName: null, applicationTagList: null, paymentInKindItems: [], awardInKindItems: [], managerFormResponsesRequired: null, managerFormResponsesCompleted: null, isAutomated: false, archiveReasonCode: null, currencyRequested: null, currencyRequestedAmountEquivalent: null, recommendedFundingAmount: null, previousGrantProgramId: null, newGrantProgramId: null, isInKindPaymentOrAward: null }; inKindAwardCreatedActivity: ApplicationActivityFromApi = { id: 834969, applicationId: 174205, applicantName: 'Tina Conway', createdByName: 'Parker Blackbaud', createdByProfileImageUrl: '', grantProgramName: 'New Form.io Program', organizationName: 'CHARLESTON PRIDE INC', workFlowLevelName: 'Review', grantProgramCycleName: 'Cycle 1', previousWorkFlowLevelName: null, previousGrantProgramCycleName: null, formName: 'Form name', activityTypeId: GrantActivityTypes.ApplicationAwarded, activityTypeName: null, comment: 'comment 2', createdDate: '2022-01-26T16:44:06.0761763Z', isMasked: false, canViewMaskedApplicantInfo: true, isNomination: false, amountRequested: 5045.0000, inKindAmountRequested: 400.0000, createdImpersonatedByName: 'Tina Conway', assignedBudgetName: null, assignedFundingSourceName: null, applicantFullNameForActivity: null, grantManagerFullNameForActivity: null, formDueDate: null, applicantPermissions: null, workflowLevelAutomationRuleName: null, fileName: null, communicationSubject: null, previousOrganizationName: null, applicationTagList: null, amount: 550.0000, paymentInKindItems: [], awardInKindItems: this.currentInKindItems, managerFormResponsesRequired: null, managerFormResponsesCompleted: null, formId: null, isAutomated: false, archiveReasonCode: null, currencyRequested: 'USD', currencyRequestedAmountEquivalent: 550.0000, recommendedFundingAmount: null, previousGrantProgramId: null, newGrantProgramId: null, grantActivityChangeJson: JSON.stringify(this.grantActivityChangeForInKind), isInKindPaymentOrAward: true }; applicantPermissions = { canManageApplicants: true, canReceiveEmails: true, isOwner: false }; applicationActivity = { records: [ this.amountRequestedUpdateActivity, this.inKindAwardCreatedActivity ], recordCount: 2 }; @BeforeEach() mock (service: ApplicationActivityService) { service['translationService'].setViewTranslationsOnState(ViewTranslationsBlank); service['applicationActivityResources'].getApplicationActivity = async () => { return this.applicationActivity; }; service['applicationActivityResources'].getMyActivity = async () => { return { records: [ this.amountRequestedUpdateActivity, this.inKindAwardCreatedActivity ], recordCount: 2 }; }; } @TestCase('should be able to get my activity') async getMyActivity (service: ApplicationActivityService) { const options: PaginationOptions = { retrieveTotalRecordCount: true, filterColumns: [], sortColumns: [{ columnName: 'createdDate', sortAscending: false }], returnAll: false, pageNumber: 1, rowsPerPage: 5 }; const result = await service.getMyActivity(options); const foundActivity = result.records.find((activity) => { return activity.id === this.inKindAwardCreatedActivity.id; }); const activityFromApi = this.inKindAwardCreatedActivity; expect(foundActivity.organizationName).to.be.equal(activityFromApi.organizationName); expect(foundActivity.currencyRequested).to.be.equal(activityFromApi.currencyRequested); expect(foundActivity.amount).to.be.equal(activityFromApi.amount); } @TestCase('should be able to adapt activity for UI - amount requested update') adaptActivityForUi (service: ApplicationActivityService) { const adapted = service.adaptActivityForUi( this.amountRequestedUpdateActivity ); expect(adapted.isInKind).to.be.false; expect(adapted.previousAmount).to.be.equal(this.previousAmtRequested + ''); expect(adapted.isAwardOrPaymentUpdate).to.be.true; expect(adapted.isPayment).to.be.false; } @TestCase('should be able to adapt activity for UI - in kind award') adaptActivityForUiInKindAward (service: ApplicationActivityService) { const adapted = service.adaptActivityForUi( this.inKindAwardCreatedActivity ); expect(adapted.isInKind).to.be.true; expect(adapted.currentInKindItems).to.deep.equal(this.currentInKindItems); expect(adapted.isAwardOrPaymentUpdate).to.be.false; expect(adapted.isPayment).to.be.false; } @TestCase('should be able to get app activity') async getAppActivity (service: ApplicationActivityService) { const result = await service.getAppActivity( this.appId, {} as PaginationOptions ); const foundActivity = result.data.records.find((activity) => { return activity.id === this.amountRequestedUpdateActivity.id; }); const activityFromApi = this.amountRequestedUpdateActivity; expect(foundActivity.workFlowLevelName).to.be.equal(activityFromApi.workFlowLevelName); expect(foundActivity.amount).to.be.equal(activityFromApi.amount); } @TestCase('should be able to get current and previous amount') getCurrentAndPreviousAmount (service: ApplicationActivityService) { const { previousAmount, currentAmount } = service.getCurrentAndPreviousAmount( this.amountRequestedUpdateActivity, this.grantActivityChangeForAmountRequested[0] ); expect(previousAmount).to.be.equal('' + this.previousAmtRequested); expect(currentAmount).to.be.equal('' + this.newAmountRequested); } @TestCase('should be able to get in kind items') getInKindItems (service: ApplicationActivityService) { const { currentInKindItems } = service.getInKindItems( this.inKindAwardCreatedActivity, this.grantActivityChangeForInKind[0], GrantActivityTypes.InKindAwardCreated ); expect(currentInKindItems).to.deep.equal(this.currentInKindItems); } @TestCase('should be able to get is payment change') isPaymentChange (service: ApplicationActivityService) { // Fail let isPaymentChange = service.isPaymentChange(GrantActivityTypes.OrganizationUpdated); expect(isPaymentChange).to.be.equal(false); isPaymentChange = service.isPaymentChange(GrantActivityTypes.NotifyOfStatusSent); expect(isPaymentChange).to.be.equal(false); isPaymentChange = service.isPaymentChange(GrantActivityTypes.AwardUpdated); expect(isPaymentChange).to.be.equal(false); // Pass isPaymentChange = service.isPaymentChange(GrantActivityTypes.PaymentDeleted); expect(isPaymentChange).to.be.equal(true); isPaymentChange = service.isPaymentChange(GrantActivityTypes.PaymentCreated); expect(isPaymentChange).to.be.equal(true); isPaymentChange = service.isPaymentChange(GrantActivityTypes.PaymentUpdated); expect(isPaymentChange).to.be.equal(true); } @TestCase('should be able to get is in kind change') isInKindChange (service: ApplicationActivityService) { // Fail let isInKindChange = service.isInKindChange(GrantActivityTypes.PaymentUpdated); expect(isInKindChange).to.be.equal(false); isInKindChange = service.isInKindChange(GrantActivityTypes.PaymentCreated); expect(isInKindChange).to.be.equal(false); isInKindChange = service.isInKindChange(GrantActivityTypes.TagAdded); expect(isInKindChange).to.be.equal(false); // Pass isInKindChange = service.isInKindChange(GrantActivityTypes.InKindAwardCreated); expect(isInKindChange).to.be.equal(true); isInKindChange = service.isInKindChange(GrantActivityTypes.InKindAmountRequestedUpdated); expect(isInKindChange).to.be.equal(true); isInKindChange = service.isInKindChange(GrantActivityTypes.InKindPaymentCreated); expect(isInKindChange).to.be.equal(true); } @TestCase('should be able to get is award or payment update') isAwardOrPaymentUpdate (service: ApplicationActivityService) { // Fail let isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.ManualApplicantFormSent); expect(isAwardOrPaymentUpdate).to.be.equal(false); isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.RecommendedFundingAmountSet); expect(isAwardOrPaymentUpdate).to.be.equal(false); isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.RevisionRequestCanceled); expect(isAwardOrPaymentUpdate).to.be.equal(false); // Pass isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.AmountRequestedUpdated); expect(isAwardOrPaymentUpdate).to.be.equal(true); isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.InKindPaymentUpdated); expect(isAwardOrPaymentUpdate).to.be.equal(true); isAwardOrPaymentUpdate = service.isAwardOrPaymentUpdate(GrantActivityTypes.PaymentUpdated); expect(isAwardOrPaymentUpdate).to.be.equal(true); } @TestCase('should be able to get is award or payment set or delete') isAwardOrPaymentSetOrDelete (service: ApplicationActivityService) { // Fail let isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.OrganizationUpdated); expect(isAwardOrPaymentSetOrDelete).to.be.equal(false); isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.AwardUpdated); expect(isAwardOrPaymentSetOrDelete).to.be.equal(false); isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.FormRevisionRequest); expect(isAwardOrPaymentSetOrDelete).to.be.equal(false); // Pass isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.RecommendedFundingAmountSet); expect(isAwardOrPaymentSetOrDelete).to.be.equal(true); isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.PaymentCreated); expect(isAwardOrPaymentSetOrDelete).to.be.equal(true); isAwardOrPaymentSetOrDelete = service.isAwardOrPaymentSetOrDelete(GrantActivityTypes.InkindAwardDeleted); expect(isAwardOrPaymentSetOrDelete).to.be.equal(true); } @TestCase('should be able to init descriptions with basic items') initDescriptionsWithBasicItems (service: ApplicationActivityService) { let descriptions = service.initDescriptionsWithBasicItems( this.inKindAwardCreatedActivity ); const hasFormName = descriptions.includes(this.inKindAwardCreatedActivity.formName); expect(hasFormName).to.be.true; const applicationTagList = 'tag 1, tag2'; descriptions = service.initDescriptionsWithBasicItems({ activityTypeId: GrantActivityTypes.TagAdded, applicationTagList } as ApplicationActivityFromApi); const hasTags = descriptions.includes(applicationTagList); expect(hasTags).to.be.true; } @TestCase('should be able to get previous to current activity type') getPreviousToCurrentActivityType (service: ApplicationActivityService) { let adaptedType = service.getPreviousToCurrentActivityType( this.inKindAwardCreatedActivity, this.grantActivityChangeForInKind[0] ); expect(adaptedType).to.be.equal(PreviousToCurrentActivityEnum.NotApplicable); adaptedType = service.getPreviousToCurrentActivityType( this.amountRequestedUpdateActivity, this.grantActivityChangeForAmountRequested[0] ); expect(adaptedType).to.be.equal(PreviousToCurrentActivityEnum.AmontRequestedUpdated); } @TestCase('should be able to adapt activity') adaptActivityType (service: ApplicationActivityService) { const activity = { ...this.amountRequestedUpdateActivity, activityTypeId: GrantActivityTypes.AwardDeleted, isInKindPaymentOrAward: true }; const newType = service.adaptActivityType( activity, null ); expect(newType).to.be.equal(GrantActivityTypes.InkindAwardDeleted); } @TestCase('should be able to get applicant permission change translation') getApplicantPermissionChangeTranslation (service: ApplicationActivityService) { const result = service.getApplicantPermissionChangeTranslation( 'can manage applicants, can receive emails' ); const expectedResult = 'Can manage applicants, Can receive emails'; expect(result).to.be.equal(expectedResult); } @TestCase('should be able to get applicant permission translation') getApplicantPermissionTranslation (service: ApplicationActivityService) { const result = service.getApplicantPermissionTranslation(this.applicantPermissions); const expectedResult = 'Can manage applicants, Can receive emails'; expect(result).to.be.equal(expectedResult); } @TestCase('should be able to get applicant permission update text') getApplicantPermissionUpdateText (service: ApplicationActivityService) { let result = service.getApplicantPermissionUpdateText( true, 'can manage applicants, can receive emails' ); const expectedResult = 'Can manage applicants, Can receive emails'; expect(result).to.be.equal(expectedResult); result = service.getApplicantPermissionUpdateText( false, '', this.applicantPermissions ); expect(result).to.be.equal(expectedResult); } @TestCase('should be able to get all app activity') async getAllAppActivity (service: ApplicationActivityService) { const records = await service.getAllAppActivity(2); const haveInKind = records.some((activity) => { return activity.id === this.inKindAwardCreatedActivity.id; }); expect(haveInKind).to.be.true; const haveAmountRequested = records.some((activity) => { return activity.id === this.amountRequestedUpdateActivity.id; }); expect(haveAmountRequested).to.be.true; } @TestCase('should be able to adapt activities for csv') async adaptActivitiesForCsv (service: ApplicationActivityService) { const activities = await service.getAllAppActivity(2); const adapted = service.adaptActivitiesForCsv(activities, false); const hasImpersonatedBy = adapted.every((activity) => { return !!activity['Impersonated by']; }); expect(hasImpersonatedBy).to.be.true; const amountRequestedActivity = adapted.find((activity) => { return activity['Activity ID'] === this.amountRequestedUpdateActivity.id; }); expect(amountRequestedActivity['Comment']).to.be.equal(this.amountRequestedUpdateActivity.comment); const inKindActivity = adapted.find((activity) => { return activity['Activity ID'] === this.inKindAwardCreatedActivity.id; }); expect(inKindActivity['Comment']).to.be.equal(this.inKindAwardCreatedActivity.comment); } }