import { Injectable } from '@angular/core'; import { GCMockModule } from '@core/mocks/gc-module.mock'; import { ReferenceFieldAPI } from '@core/typings/api/reference-fields.typing'; import { BaseApplication } from '@core/typings/application.typing'; import { AdHocReportingUI } from '@core/typings/ui/ad-hoc-reporting.typing'; import { ReferenceFieldsUI } from '@core/typings/ui/reference-fields.typing'; import { CreateEditFormModalResponse } from '@features/configure-forms/create-edit-form-modal/create-edit-form-modal.component'; import { BaseApplicationForLogic, Form, FormAudience, FormDefinitionComponent, FormDefinitionForUi, FormFromApi, FormTypes, ReportFieldDataOptions, SaveNewFormResponse } from '@features/configure-forms/form.typing'; import { CustomDataTable, PicklistDataType } from '@features/custom-data-tables/custom-data-tables.typing'; import { FormulaEvaluationType, FormulaStepValueType } from '@features/formula-builder/formula-builder.typing'; import { ConditionalLogicResultType, EvaluationType, GlobalLogicGroup, LogicColumn, LogicColumnDisplay, LogicCondition, NestedPropColumn } from '@features/logic-builder/logic-builder.typing'; import { FilterModalTypes } from '@yourcause/common'; import { BeforeEach, Spec, TestCase } from '@yourcause/test-decorators'; import { DescribeAngularService } from '@yourcause/test-decorators/angular'; import { expect } from 'chai'; import { isEqual } from 'lodash'; import { FormLogicService } from './form-logic.service'; @Injectable({ providedIn: 'root' }) @DescribeAngularService(FormLogicService, { imports: [ GCMockModule ] }) export class FormLogicServiceSpec implements Spec { formId = 2353; revisionId = 13266; cdtId1 = 1; cdtId2 = 2; cdtId3 = 3; cdtGuid1 = 'guid1'; cdtGuid2 = 'guid2'; cdtGuid3 = 'guid3'; uniqueId = '1234'; textFieldKey = 'textField'; textAreaKey = 'textArea'; numberKey = 'number'; tableKey1 = 'tableKey1'; tableKey2 = 'tableKey2'; applicantTextField: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'Text', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.TextField, key: this.textFieldKey, supportsMultiple: true, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 1, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; applicantTextArea: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'Text Area', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.TextArea, key: this.textAreaKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 2, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; applicantNumber: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'Number', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.Number, key: this.numberKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 3, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; fileUploadKey = 'fileUploadKey'; fileUploadId = 36; applicantFile: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'File Upload', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.FileUpload, key: this.fileUploadKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: this.fileUploadId, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; picklistKey = 'picklistKey'; picklistId = 1353; picklistField: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: this.cdtGuid3, name: 'picklist', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.CustomDataTable, key: this.picklistKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: this.picklistId, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; fileUploadForTableKey = 'fileUploadForTableKey'; fileUploadIdForTable = 325; fileUploadFieldForTable: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'File Upload For Table 1', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.FileUpload, key: this.fileUploadForTableKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: this.fileUploadIdForTable, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: true, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; tableField1: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'Table 1', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.Table, key: this.tableKey1, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 13, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; tableField2: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'Table 2', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.Table, key: this.tableKey2, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 12, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; externalApiKey = 'externalApiKey'; externalApiField: ReferenceFieldAPI.ReferenceFieldDisplayModel = { customDataTableGuid: '', name: 'External Api', description: '', type: ReferenceFieldsUI.ReferenceFieldTypes.ExternalAPI, key: this.externalApiKey, supportsMultiple: false, categoryId: 0, formAudience: FormAudience.APPLICANT, referenceFieldId: 187, formCount: 1, usedOnReports: false, createdBy: '', createDate: '', updatedBy: '', aggregateType: null, updateDate: '', parentReferenceFieldId: null, isEncrypted: false, isMasked: false, tableAllowsImport: false, isSingleResponse: true, isTableField: false, aggregateTableReferenceFieldId: null, referenceFieldTableId: null, standardComponentIsPublished: true, isStandardProductField: false, subsetCollectionType: null }; expectedResult = 2352.23; tabs: FormDefinitionForUi[] = [{ tabName: 'tab1', logic: null, uniqueId: this.uniqueId, components: [{ type: `referenceFields-${this.textFieldKey}`, key: this.textFieldKey, conditionalValue: [ { evaluationType: 0, useAnd: false, identifier: '0c2b80d02da645fda27ce41625d483c7d315ee955ada45d29c41e51085983bb6', conditions: [ { sourceColumn: [ 'referenceFields', this.textFieldKey ], comparison: 'eq', value: true, useAnd: false, identifier: '8d85af50598a4487b559e06eb7079d9d48ad2f8052d94068b8748012c3873b8a' } as any ], result: this.expectedResult, resultType: ConditionalLogicResultType.STATIC_VALUE } ], validate: { required: true } }, { type: 'amountRequested', key: 'amountRequested', validate: { required: true }, formula: { property: 'referenceFields.budgetAmountTotal', step: { type: FormulaEvaluationType.Add, values: [ { value: 'referenceFields.amount2', type: FormulaStepValueType.ParentValue }, { value: 'referenceFields.budgetAmountQ2', type: FormulaStepValueType.ParentValue }, { type: FormulaStepValueType.ParentValue, value: 'referenceFields.budgetAmountQ3' }, { type: FormulaStepValueType.ParentValue, value: 'referenceFields.budgetAmountQ4' } ] } } }], index: 0 }, { tabName: 'tab2', logic: null, uniqueId: '1353464', components: [{ type: 'inKindItems', key: 'inKindItems', validate: { required: false }, conditionalLogic: { evaluationType: EvaluationType.ConditionallyTrue, useAnd: false, identifier: 'cfb9fded1d714d4bbe6575809b56527a4ffdb95f00ec4327aa352169a59d7b33', conditions: [ { sourceColumn: [ 'application', 'amountRequested' ], comparison: 'gt', value: 500, useAnd: false, identifier: '11901a36f65b4e86a86d2aca074ea7bda9ece5a4923c4a64af5b839eca6dbf59' } ] } as any }], index: 1 }]; formFromApi: FormFromApi = { title: '', name: 'Test Form', path: '', id: this.formId, description: '', createdDate: '', updatedDate: '', formDefinition: this.tabs, formType: FormTypes.REQUEST, version: 1, canModify: true, availableForTranslation: true, defaultLanguageId: '', externalApiRequestIds: [], referenceFieldIds: [], requireSignature: false, signatureDescription: '' }; customDataTables: CustomDataTable[] = [{ id: this.cdtId1, name: 'Parent Table', guid: this.cdtGuid1, createdDate: '', updatedDate: '', createdBy: '', updatedBy: '', isSystem: false, hasOptions: true, defaultLanguageId: '', dataType: PicklistDataType.Text, parentPicklistId: null }, { id: this.cdtId2, name: 'Child Table', guid: this.cdtGuid2, createdDate: '', updatedDate: '', createdBy: '', updatedBy: '', isSystem: false, hasOptions: true, dataType: PicklistDataType.Text, defaultLanguageId: '', parentPicklistId: this.cdtId1 }, { id: this.cdtId3, name: 'Standalone Table', guid: this.cdtGuid3, createdDate: '', updatedDate: '', createdBy: '', dataType: PicklistDataType.Text, updatedBy: '', isSystem: false, hasOptions: true, defaultLanguageId: '', parentPicklistId: null }]; form: Form = { title: '', name: 'Test Form', path: '', id: this.formId, description: '', createdDate: '', updatedDate: '', formDefinition: this.tabs, formType: FormTypes.REQUEST, version: 1, canModify: true, availableForTranslation: true, defaultLanguageId: '', externalApiRequestIds: [], referenceFieldIds: [], requireSignature: false, signatureDescription: '' }; refComponent = { key: this.textFieldKey, label: 'Text field', type: `referenceFields-${this.textFieldKey}` } as FormDefinitionComponent; numberCondition: LogicCondition> = { sourceColumn: ['referenceFields', this.numberKey] as NestedPropColumn, comparison: FilterModalTypes.equals, identifier: '', useAnd: true, value: '2' }; conditionalLogic: GlobalLogicGroup = { conditions: [ this.numberCondition ], useAnd: true, identifier: '', evaluationType: EvaluationType.ConditionallyTrue }; baseApplication = { formType: FormTypes.REQUEST, applicationId: 1, applicationFormId: 2, formId: 3, revisionId: 4, amountRequested: 400, currencyRequestedAmountEquivalent: 400, amountRequestedForEdit: 400, currencyRequested: 'USD', reviewerRecommendedFundingAmount: 346, designation: 'designation', careOf: 'care of', programId: 5, reportFieldResponse: null, employeeInfo: null, specialHandling: null, defaultSpecialHandling: null, referenceFields: { textKey1: 'text answer', picklistKey: 'picklist answere' }, inKindItems: [] } as BaseApplication; reportFieldResponse = { application: { id: 1235 } as any, form: { applicationFormId: 43643 } as any, referenceFields: [{ referenceFieldId: 1, referenceFieldKey: 'multiTextKey', value: '', dateValue: '', type: ReferenceFieldsUI.ReferenceFieldTypes.TextArea }, { referenceFieldId: 2, referenceFieldKey: 'fileUploadKey', value: 'https://yourcausegrantsqa.com/management/download-file?applicationId=166993&applicationFormId=163915&fileId=762&fileName=logo.jpg', dateValue: null, numericValue: null, type: ReferenceFieldsUI.ReferenceFieldTypes.FileUpload, values: [], isEncrypted: false, isReferenceFieldMasked: false, currencyValue: null, supportsMultiple: false, file: { fileId: 15, fileName: 'File name', value: 'https://yourcausegrantsqa.com/management/download-file?applicationId=166993&applicationFormId=163915&fileId=7624&fileName=logo.jpg' }, files: [] }] } as AdHocReportingUI.ReportResponseRow; currentDef: FormDefinitionForUi[] = [{ tabName: 'First Page', components: [ this.refComponent, { type: `referenceFields-${this.picklistKey}`, key: this.picklistKey } as FormDefinitionComponent ], uniqueId: '1', index: 0, logic: null }]; @BeforeEach() async mock (service: FormLogicService) { service['formLogicResources']['getForm'] = async () => { return this.formFromApi; }; service['customDataTableService']['set']( 'customDataTables', this.customDataTables ); service['referenceFieldService'].setAllReferenceFields([ this.applicantTextField, this.applicantNumber, this.applicantTextArea, this.tableField1, this.tableField2, this.applicantFile, this.fileUploadFieldForTable, this.externalApiField, this.picklistField ]); service['referenceFieldService']['referenceFieldsResources'].getTableFields = async () => { return [{ referenceFieldId: this.fileUploadIdForTable, referenceFieldName: this.fileUploadForTableKey, label: 'File upload for table', isRequired: false, showInTable: true, columnOrder: 1, aggregateColumnReferenceFieldId: null }]; }; await service['referenceFieldService'].getTableFields( this.tableField1.referenceFieldId ); } @TestCase('should be able to fetch form detail') async fetchFormDetail (service: FormLogicService) { let fetched = false; service['formLogicResources'].getForm = async () => { fetched = true; return this.formFromApi; }; await service.fetchFormDetail(this.formId, this.revisionId); expect(fetched).to.be.true; } @TestCase('should be able to adapt form for tabs') adaptFormForTabs (service: FormLogicService) { const adapted = service.adaptFormForTabs(this.formFromApi, 1, 2); const formDefIsArray = adapted.formDefinition instanceof Array; expect(formDefIsArray).to.be.true; expect(adapted.formDefinition.length).to.be.equal(this.tabs.length); } @TestCase('should be able to adapt form definition for tabs') adaptFormDefinitionForTabs (service: FormLogicService) { const adapted = service.adaptFormDefinitionForTabs( { components: [], logic: null, tabName: '' }, 1, 2 ); const isArray = adapted.formDefinition instanceof Array; expect(isArray).to.be.true; expect(adapted.formDefinition.length).to.be.equal(1); } @TestCase('should be able to adapt components for logic') adaptComponentsForLogic (service: FormLogicService) { const formDef: FormDefinitionForUi[] = [{ tabName: 'tab1', logic: null, uniqueId: this.uniqueId, index: 0, components: [{ type: 'amountRequested', validate: { custom: 'custom' } } as FormDefinitionComponent] }]; let didLog = false; service['logger'].log = () => { didLog = true; }; service.adaptComponentsForLogic(formDef, this.formId, this.revisionId); expect(didLog).to.be.true; } @TestCase('should be able to adapt component values - removal') adaptInitialComponentValues (service: FormLogicService) { const tableComp = { type: `referenceFields-${this.tableField1.key}`, value: 'test', isHidden: true, hiddenFromParent: true, defaultVal: 'defaultVal', clearOnHide: true } as FormDefinitionComponent; service.adaptInitialComponentValues(tableComp); const removedValue = !tableComp.value; const removedDefaultVal = !tableComp.defaultVal; const removedIsHidden = !tableComp.isHidden && !tableComp.hiddenFromParent; const removedClearOnHide = !tableComp.clearOnHide; expect(removedValue).to.be.true; expect(removedDefaultVal).to.be.true; expect(removedIsHidden).to.be.true; expect(removedClearOnHide).to.be.true; } @TestCase('should be able to adapt component values - pattern and mask') adaptInitialComponentValues2 (service: FormLogicService) { const supportsMultipleComp = { type: `referenceFields-${this.applicantTextField.key}`, value: 'test', inputMask: 'asdf', validate: { pattern: 'asdf' } } as FormDefinitionComponent; service.adaptInitialComponentValues(supportsMultipleComp); const removedPattern = !supportsMultipleComp.validate.pattern; const removedMask = !supportsMultipleComp.inputMask; expect(removedPattern).to.be.true; expect(removedMask).to.be.true; } @TestCase('should be able to get form detail') getFormDetail (service: FormLogicService) { service.setFormDetail(this.formId, this.revisionId, this.form); const detail = service.getFormDetail(this.formId, this.revisionId); expect(detail).to.deep.equal(this.form); } @TestCase('should be able to set form detail') setFormDetail (service: FormLogicService) { service.setFormDetail(this.formId, this.revisionId, this.form); const detail = service.formDetail[+('' + this.formId + this.revisionId)]; expect(detail).to.deep.equal(this.form); } @TestCase('should be able to handle create form - pass') async handleCreateFormPass (service: FormLogicService) { const payload: CreateEditFormModalResponse = { formName: 'Request', formType: FormTypes.REQUEST, defaultFormLang: 'en-US', startFromTemplate: null, requireSignature: true, signatureDescription: '', revisionId: 1235 }; const newId = 253523; service['formLogicResources'].saveForm = async () => { return { id: newId } as SaveNewFormResponse; }; const result = await service.handleCreateForm(payload); const expectedRoute = `/management/program-setup/forms/${newId}/draft`; expect(result).to.be.equal(expectedRoute); } @TestCase('should be able to handle create form - fail') async handleCreateFormFail (service: FormLogicService) { const payload: CreateEditFormModalResponse = { formName: 'Request', formType: FormTypes.REQUEST, defaultFormLang: 'en-US', startFromTemplate: null, requireSignature: true, signatureDescription: '', revisionId: 6436 }; service['formLogicResources'].saveForm = async () => { /* eslint-disable no-throw-literal */ throw { error: { message: 'Some error' } }; }; const result = await service.handleCreateForm(payload); expect(result).to.be.equal(null); } @TestCase('should be able to get form details for create from a template') async getFormDetailsForCreateFromTemplate (service: FormLogicService) { const result = await service.getFormDetailsForCreate({ name: 'Standard Product Template', formId: this.formId, revisionId: this.revisionId, defaultFormLang: 'en-US', description: '' }); const def = result.formDefinition[0]; const expectedDef = this.tabs[0]; expect(result.formDefinition.length).to.be.equal(this.tabs.length); expect(def.tabName).to.be.equal(expectedDef.tabName); expect(def.components).to.deep.equal(expectedDef.components); } @TestCase('should be able to handle save form error') handleSaveFormError (service: FormLogicService) { let result = service.handleSaveFormError({}); // no pending error expect(result).to.be.false; result = service.handleSaveFormError({ error: { message: 'This form already has a draft revision pending to be published' } }); // has pending error expect(result).to.be.true; } @TestCase('should be able to get available columns for logic modal') getReferenceFieldLogicColumnDisplay (service: FormLogicService) { let response = service.getReferenceFieldLogicColumnDisplay( this.refComponent.type, this.refComponent.label ); expect(response.type).to.be.equal('multiValueText'); response = service.getReferenceFieldLogicColumnDisplay( 'amountRequested', 'Amount requested' ); // No response because this function is only for reference fields, not standard const noResponse = !response; expect(noResponse).to.be.equal(true); response = service.getReferenceFieldLogicColumnDisplay( `referenceFields-${this.picklistKey}`, 'Picklist label' ); expect(response.type).to.be.equal('multiListFuzzyText'); } @TestCase('should be able to get available columns for logic modal') getAvailableColumnsForLogicModal (service: FormLogicService) { const formDefs: FormDefinitionForUi[] = [{ tabName: 'One', components: [{ type: `referenceFields-${this.textFieldKey}` } as FormDefinitionComponent, { type: `referenceFields-${this.tableKey1}` } as FormDefinitionComponent, { type: `referenceFields-${this.textAreaKey}` } as FormDefinitionComponent], logic: null, index: 0, uniqueId: '1' }, { tabName: 'Two', components: [{ type: `referenceFields-${this.tableKey2}` } as FormDefinitionComponent, { type: 'designation' } as FormDefinitionComponent, { type: 'amountRequested' } as FormDefinitionComponent], logic: null, index: 1, uniqueId: '2' }]; const availableColumns = service.getAvailableColumnsForLogicModal( formDefs, 1, FormAudience.APPLICANT, false, false ); // Should filter out amount requested (bc it exists on tab we are fetching for) // Should filter out tables because it does not support them const hasAmountRequested = availableColumns.some((col) => { return col.column[1] === 'amountRequested'; }); expect(hasAmountRequested).to.be.false; const hasTables = availableColumns.some((col) => { return col.column[1] === this.tableKey2; }); expect(hasTables).to.be.false; const hasTextFieldAndAreaKeys = availableColumns.some((col) => { return col.column[1] === this.textFieldKey || col.column[1] === this.textAreaKey; }); expect(hasTextFieldAndAreaKeys).to.be.true; } @TestCase('should be able to add other column options - text') addOtherColumnOptions (service: FormLogicService) { const textFieldCol: LogicColumnDisplay = { label: '', column: ['referenceFields', this.textFieldKey] as NestedPropColumn, type: 'text', otherColumnOptions: [] }; const textAreaCol: LogicColumnDisplay = { label: '', column: ['referenceFields', this.textAreaKey] as NestedPropColumn, type: 'text', otherColumnOptions: [] }; const availableColumns: LogicColumnDisplay[] = [ textFieldCol, textAreaCol ]; service.addOtherColumnOptions(availableColumns); // Should have other column option of text area, bc they are the same type const hasCorrectOptions = textFieldCol.otherColumnOptions.some((opt) => { return isEqual(textAreaCol.column, opt.value); }); expect(hasCorrectOptions).to.be.true; } @TestCase('should be able to add other column options - number') addOtherColumnOptionsNumber (service: FormLogicService) { const numberFieldCol: LogicColumnDisplay = { label: '', column: ['referenceFields', this.numberKey] as NestedPropColumn, type: 'number', otherColumnOptions: [] }; const currencyCol: LogicColumnDisplay = { label: '', column: ['referenceFields', 'currencyKey'] as NestedPropColumn, type: 'currency', otherColumnOptions: [] }; const availableColumns: LogicColumnDisplay[] = [ numberFieldCol, currencyCol ]; service.addOtherColumnOptions(availableColumns); // Should have other column option of currency, bc number and currency are compatible filter types const hasCorrectOptions = numberFieldCol.otherColumnOptions.some((opt) => { return isEqual(currencyCol.column, opt.value); }); expect(hasCorrectOptions).to.be.true; } @TestCase('should be able to add other column options - no options') addOtherColumnOptionsNoOptions (service: FormLogicService) { const picklistMultiCol: LogicColumnDisplay = { label: '', column: ['referenceFields', 'picklistMulti'] as NestedPropColumn, type: 'multi-list', filterOptions: [], otherColumnOptions: [] }; const textAreaCol: LogicColumnDisplay = { label: '', column: ['referenceFields', this.textAreaKey] as NestedPropColumn, type: 'text', otherColumnOptions: [] }; const availableColumns: LogicColumnDisplay[] = [ picklistMultiCol, textAreaCol ]; service.addOtherColumnOptions(availableColumns); // Should not have other column option of text area, bc they are not the same type const picklistHasNoOptions = picklistMultiCol.otherColumnOptions.length === 0; expect(picklistHasNoOptions).to.be.true; const textAreaHasNoOptions = textAreaCol.otherColumnOptions.length === 0; expect(textAreaHasNoOptions).to.be.true; } @TestCase('should be able to get application fields for logic - applicant') getApplicationFieldsForLogicApplicant (service: FormLogicService) { const fields = service.getApplicationFieldsForLogic(FormAudience.APPLICANT); const hasAmountRequested = fields.some((field) => { return isEqual(field.column, ['application', 'amountRequested']); }); const hasDesignation = fields.some((field) => { return isEqual(field.column, ['application', 'designation']); }); expect(hasAmountRequested).to.be.true; expect(hasDesignation).to.be.true; } @TestCase('should be able to get application fields for logic - manager') getApplicationFieldsForLogicManager (service: FormLogicService) { const fields = service.getApplicationFieldsForLogic(FormAudience.MANAGER); const hasDecision = fields.some((field) => { return isEqual(field.column, ['application', 'decision']); }); const hasRecommendedFunding = fields.some((field) => { return isEqual(field.column, ['application', 'reviewerRecommendedFundingAmount']); }); expect(hasDecision).to.be.true; expect(hasRecommendedFunding).to.be.true; } @TestCase('should be able to get application applicant fields for logic') getApplicantApplicationFieldsForLogic (service: FormLogicService) { const fields = service.getApplicantApplicationFieldsForLogic(); const amountRequested = fields.find((field) => { return isEqual(field.column, ['application', 'amountRequested']); }); const designation = fields.find((field) => { return isEqual(field.column, ['application', 'designation']); }); expect(amountRequested.type).to.be.equal('number'); expect(designation.type).to.be.equal('text'); } @TestCase('should be able to get manager application fields for logic') getManagerApplicationFieldsForLogic (service: FormLogicService) { const fields = service.getManagerApplicationFieldsForLogic(); const decision = fields.find((field) => { return isEqual(field.column, ['application', 'decision']); }); const recommendedFunding = fields.find((field) => { return isEqual(field.column, ['application', 'reviewerRecommendedFundingAmount']); }); expect(decision.type).to.be.equal('multi-list'); expect(recommendedFunding.type).to.be.equal('number'); } @TestCase('should be able to get is matching filter type') isMatchingFilterType (service: FormLogicService) { let isMatching = service.isMatchingFilterType('number', 'currency'); expect(isMatching).to.be.true; isMatching = service.isMatchingFilterType('text', 'text'); expect(isMatching).to.be.true; isMatching = service.isMatchingFilterType('text', 'number'); expect(isMatching).to.be.false; } @TestCase('should be able to get available columns for reference field component') getAvailableColumnsForRefComponent (service: FormLogicService) { const refComponent = { key: this.textFieldKey, type: `referenceFields-${this.textFieldKey}` } as FormDefinitionComponent; const { availableColumns } = service.getAvailableLogicColumnsForComponent( refComponent, true, this.currentDef, 0, FormAudience.APPLICANT, false ); // Should have designation and amount requested available const hasAmountRequested = availableColumns.some((col) => { return col.column[1] === 'amountRequested'; }); expect(hasAmountRequested).to.be.true; const hasDesignation = availableColumns.some((col) => { return col.column[1] === 'designation'; }); expect(hasDesignation).to.be.true; } @TestCase('should be able to get available columns for standard app component') getAvailableColumnsForStandardComponent (service: FormLogicService) { const standardComponent = { key: 'amountRequested', type: 'amountRequested' } as FormDefinitionComponent; const { availableColumns } = service.getAvailableLogicColumnsForComponent( standardComponent, true, this.currentDef, 0, FormAudience.APPLICANT, false ); // Should have ref component and designation available const hasRefField = availableColumns.some((col) => { return col.column[1] === this.textFieldKey; }); expect(hasRefField).to.be.true; const hasDesignation = availableColumns.some((col) => { return col.column[1] === 'designation'; }); expect(hasDesignation).to.be.true; } @TestCase('should be able to get ref field column for logic') getRefFieldColumnForLogic (service: FormLogicService) { const refComponent = { key: this.textFieldKey, type: `referenceFields-${this.textFieldKey}`, conditionalLogic: this.conditionalLogic } as FormDefinitionComponent; const result = service.getRefFieldColumnLogic(refComponent); const expectedFieldArray = ['referenceFields', this.textFieldKey]; expect(result[0]).to.deep.equal(expectedFieldArray); expect(result[1].visibilityGroup).to.deep.equal(this.conditionalLogic); } @TestCase('should be able to get app comp column for logic') getAppCompColumnForLogic (service: FormLogicService) { const standardComponent = { key: 'amountRequested', type: 'amountRequested', conditionalLogic: this.conditionalLogic } as FormDefinitionComponent; const result = service.getAppCompColumnLogic(standardComponent); const expectedFieldArray = ['application', 'amountRequested']; expect(result[0]).to.deep.equal(expectedFieldArray); expect(result[1].visibilityGroup).to.deep.equal(this.conditionalLogic); } @TestCase('should be able to get layout column for logic') getLayoutColumnForLogic (service: FormLogicService) { const layoutComponent = { key: 'well', type: 'well', conditionalLogic: this.conditionalLogic } as FormDefinitionComponent; const result = service.getLayoutColumnLogic(layoutComponent); const expectedFieldArray = ['layoutComponents', 'well']; expect(result[0]).to.deep.equal(expectedFieldArray); expect(result[1].visibilityGroup).to.deep.equal(this.conditionalLogic); } @TestCase('should be able to get report field column for logic') getReportFieldColumnForLogic (service: FormLogicService) { const reportField = { key: 'reportFieldKey', type: 'reportField', reportFieldDataOptions: { reportFieldObject: 'object', reportFieldDisplay: 'display' }, conditionalLogic: this.conditionalLogic } as FormDefinitionComponent; const result = service.getReportFieldColumnLogic(reportField); const expectedFieldArray = ['reportFieldResponse', 'object', 'display']; expect(result[0]).to.deep.equal(expectedFieldArray); expect(result[1].visibilityGroup).to.deep.equal(this.conditionalLogic); } @TestCase('should be able to get page template options for a given formId') async testGettingPageOptionsForAFormId (service: FormLogicService) { service.setFormDetail(this.formId, this.revisionId, this.form); const pageTemplateOptions = await service.getPageTemplateOptions( this.formId, this.revisionId ); const everyTabHasOption = this.tabs.every((tab) => { return pageTemplateOptions.some((option) => { return option.value === tab.uniqueId; }); }); expect(everyTabHasOption).to.be.true; } @TestCase('should be able to init form definition logic') initFormDefinitionLogic (service: FormLogicService) { const record = service.getRecordForLogic( this.baseApplication, this.reportFieldResponse ); expect(record.application).to.deep.equal(this.baseApplication); expect(record.referenceFields).to.deep.equal(this.baseApplication.referenceFields); } @TestCase('should be able to get logic context - conditional visibility') getLogicContextConditionalVisibility (service: FormLogicService) { const { logicGroups } = service.getLogicContext( this.tabs, this.baseApplication, this.reportFieldResponse ); const foundLogicGroup = logicGroups.find((group) => { return isEqual( group[0], ['application', 'inKindItems'] ); }); const hasCorrectSettings = foundLogicGroup[1].visibilityGroup.evaluationType === EvaluationType.ConditionallyTrue; expect(!!foundLogicGroup).to.be.true; expect(hasCorrectSettings).to.be.true; } @TestCase('should be able to get logic context - formulas') getLogicContextFormula (service: FormLogicService) { const { formulas } = service.getLogicContext( this.tabs, this.baseApplication, this.reportFieldResponse ); const foundFormula = formulas[0]; expect(foundFormula.property).to.be.equal('amountRequested'); expect(foundFormula.step.type).to.be.equal(FormulaEvaluationType.Add); } @TestCase('should be able to get logic context - conditional values') getLogicContextConditionalValues (service: FormLogicService) { const { logicGroups } = service.getLogicContext( this.tabs, this.baseApplication, this.reportFieldResponse ); const foundLogicGroup = logicGroups.find((group) => { return isEqual( group[0], ['referenceFields', this.textFieldKey] ); }); expect(!!foundLogicGroup).to.be.true; const setValueDetails = foundLogicGroup[1].conditionalValueGroups; const foundCorrectRule = setValueDetails.find((detail) => { return detail.result === this.expectedResult; }); expect(!!foundCorrectRule).to.be.true; } @TestCase('should be able to get record for logic') getRecordForLogic (service: FormLogicService) { const record = service.getRecordForLogic( this.baseApplication, this.reportFieldResponse ); expect(record.application).to.deep.equal(this.baseApplication); expect(record.referenceFields).to.deep.equal(this.baseApplication.referenceFields); } @TestCase('should be able to get report field columns for logic') getReportFieldColumnsForLogic (service: FormLogicService) { const reportFieldDisplay = 'totalCashPaymentAmount'; service.setReportFieldLogicColumns([{ column: ['reportFieldResponse', 'application', reportFieldDisplay] as LogicColumn, label: 'payment amount', otherColumnOptions: [ { label: 'Cash amount requested', value: ['application', 'amountRequested'] }, { label: 'Cash Award Amt plus payment amt', value: ['referenceFields', 'numberOfEvents'] } ] }] as LogicColumnDisplay[]); const reportFieldDataOptions: ReportFieldDataOptions = { reportFieldObject: 'object', reportFieldDisplay }; const comp = { type: 'reportField', key: 'reportKey', reportFieldDataOptions: JSON.stringify(reportFieldDataOptions) as any } as FormDefinitionComponent; const reportFieldColumnsForLogic = service.getReportFieldColumnsForLogic([comp]); const reportFieldColumnsReturnedIncludeFieldOnForm = reportFieldColumnsForLogic.every((reportCol) => { return reportCol.column.includes(reportFieldDataOptions.reportFieldDisplay as any); }); expect(reportFieldColumnsReturnedIncludeFieldOnForm).to.be.true; } }