From dce0b10e14b6febcbab9c2a8445e4af86b40e35f Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Tue, 16 Jul 2024 11:12:04 -0500 Subject: [PATCH 1/4] FIO-8537: Fixing issues with the filter process to handle nested components properly. --- src/process/filter/__tests__/filter.test.ts | 10 ++- src/process/filter/index.ts | 77 ++++++++------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/process/filter/__tests__/filter.test.ts b/src/process/filter/__tests__/filter.test.ts index a6cea9f3..71015bb6 100644 --- a/src/process/filter/__tests__/filter.test.ts +++ b/src/process/filter/__tests__/filter.test.ts @@ -23,7 +23,7 @@ it('Should not filter empty array value for dataGrid component', async () => { }; const context: any = generateProcessorContext(dataGridComp, data); filterProcessSync(context); - expect(context.scope.filter).to.deep.equal({'dataGrid': {'compModelType': 'array', 'include': true}}); + expect(context.scope.filter).to.deep.equal({'dataGrid': {'compModelType': 'array', 'include': true, value: []}}); }); it('Should not filter empty array value for editGrid component', async () => { @@ -46,7 +46,7 @@ it('Should not filter empty array value for editGrid component', async () => { }; const context: any = generateProcessorContext(editGridComp, data); filterProcessSync(context); - expect(context.scope.filter).to.deep.equal({'editGrid': {'compModelType': 'array', 'include': true}}); + expect(context.scope.filter).to.deep.equal({'editGrid': {'compModelType': 'array', 'include': true, value: []}}); }); it('Should not filter empty array value for datTable component', async () => { @@ -69,5 +69,9 @@ it('Should not filter empty array value for datTable component', async () => { }; const context: any = generateProcessorContext(dataTableComp, data); filterProcessSync(context); - expect(context.scope.filter).to.deep.equal({'dataTable': {'compModelType': 'array', 'include': true}}); + expect(context.scope.filter).to.deep.equal({'dataTable': {'compModelType': 'array', 'include': true, value: []}}); +}); + +it('Should filter nested fields within a nested form correctly.', async () => { + }); diff --git a/src/process/filter/index.ts b/src/process/filter/index.ts index 423b03f2..4e022704 100644 --- a/src/process/filter/index.ts +++ b/src/process/filter/index.ts @@ -1,21 +1,12 @@ -import { - FilterContext, - FilterScope, - ProcessorFn, - ProcessorFnSync, - ProcessorInfo, -} from 'types'; -import set from 'lodash/fp/set'; -import { Utils } from 'utils'; -import { get } from 'lodash'; -import { getComponentAbsolutePath } from 'utils/formUtil'; -export const filterProcessSync: ProcessorFnSync = ( - context: FilterContext -) => { +import { FilterContext, FilterScope, ProcessorFn, ProcessorFnSync, ProcessorInfo } from "types"; +import { set } from 'lodash'; +import { Utils } from "utils"; +import { get, isObject } from "lodash"; +import { getComponentAbsolutePath } from "utils/formUtil"; +export const filterProcessSync: ProcessorFnSync = (context: FilterContext) => { const { scope, component } = context; let { value } = context; const absolutePath = getComponentAbsolutePath(component); - if (!scope.filter) scope.filter = {}; if (value !== undefined) { const modelType = Utils.getModelType(component); @@ -24,22 +15,22 @@ export const filterProcessSync: ProcessorFnSync = ( scope.filter[absolutePath] = { compModelType: modelType, include: true, - value: { data: {} }, + value: { data: {} } }; break; case 'array': scope.filter[absolutePath] = { compModelType: modelType, include: true, + value: [] }; break; case 'object': - if (component.type !== 'container') { - scope.filter[absolutePath] = { - compModelType: modelType, - include: true, - }; - } + scope.filter[absolutePath] = { + compModelType: modelType, + include: true, + value: (component.type === 'address') ? false : {} + }; break; default: scope.filter[absolutePath] = { @@ -51,37 +42,25 @@ export const filterProcessSync: ProcessorFnSync = ( } }; -export const filterProcess: ProcessorFn = async ( - context: FilterContext -) => { +export const filterProcess: ProcessorFn = async (context: FilterContext) => { return filterProcessSync(context); }; -export const filterPostProcess: ProcessorFnSync = ( - context: FilterContext -) => { - const { scope, component, submission } = context; - let filtered: Record = {}; +export const filterPostProcess: ProcessorFnSync = (context: FilterContext) => { + const { scope, submission } = context; + const filtered = {}; for (const path in scope.filter) { - let value = get(submission?.data, path) as any; - const pathFilter = scope.filter[path]; - - if (pathFilter.compModelType === 'array') { - // special case for array, if it's empty, set it to empty array - if(value.length === 0) { - filtered[path] = [] - } - continue; - } else if (pathFilter) { - // when it's a dataModel Object, don't set values directly on the data object, let child fields do that. - // it can have extra data on updates, so pass all other values except data - // standard lodash set function will mutate original value, using the functional version so it doesn't - if (pathFilter.compModelType === 'dataObject') { - const { data, ...rest } = value; - filtered = set(path, rest)(filtered); - } else { - filtered = set(path, value)(filtered); + if (scope.filter[path].include) { + let value = get(submission?.data, path); + if (scope.filter[path].value) { + if (isObject(value) && scope.filter[path].value?.data) { + value = { ...value, ...scope.filter[path].value }; + } + else { + value = scope.filter[path].value; + } } + set(filtered, path, value); } } context.data = filtered; @@ -93,4 +72,4 @@ export const filterProcessInfo: ProcessorInfo = { processSync: filterProcessSync, postProcess: filterPostProcess, shouldProcess: (context: FilterContext) => true, -}; +}; \ No newline at end of file From 3fc51ee585688800b9d734d005055a2a46ab1b4a Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Tue, 16 Jul 2024 11:14:12 -0500 Subject: [PATCH 2/4] Adding the process tests to test for proper filtering of nested components. --- src/process/__tests__/process.test.ts | 250 ++++++++++++++++++++------ 1 file changed, 193 insertions(+), 57 deletions(-) diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index 391c376c..4e5ed176 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -831,29 +831,7 @@ describe('Process Tests', () => { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', pathName: '/', onLine: true, - headers: { - host: 'localhost:3000', - connection: 'keep-alive', - 'content-length': '9020', - pragma: 'no-cache', - 'cache-control': 'no-cache', - 'sec-ch-ua': - '"Chromium";v="122", "Not(A:Brand";v="24", "Brave";v="122"', - accept: 'application/json', - 'content-type': 'application/json', - 'sec-ch-ua-mobile': '?0', - 'user-agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', - 'sec-ch-ua-platform': '"macOS"', - 'sec-gpc': '1', - 'accept-language': 'en-US,en', - origin: 'http://localhost:3000', - 'sec-fetch-site': 'same-origin', - 'sec-fetch-mode': 'cors', - 'sec-fetch-dest': 'empty', - referer: 'http://localhost:3000/', - 'accept-encoding': 'gzip, deflate, br', - }, + }, data: { number: 23, @@ -969,40 +947,7 @@ describe('Process Tests', () => { }, owner: '65ea3601c3792e416cabcb2a', access: [], - metadata: { - timezone: 'America/Chicago', - offset: -360, - origin: 'http://localhost:3000', - referrer: '', - browserName: 'Netscape', - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', - pathName: '/', - onLine: true, - headers: { - 'accept-language': 'en-US,en', - 'cache-control': 'no-cache', - connection: 'keep-alive', - origin: 'http://localhost:3000', - pragma: 'no-cache', - referer: 'http://localhost:3000/', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-origin', - 'sec-gpc': '1', - 'user-agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36', - accept: 'application/json', - 'content-type': 'application/json', - 'sec-ch-ua': - '"Chromium";v="122", "Not(A:Brand";v="24", "Brave";v="122"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"macOS"', - host: 'localhost:3000', - 'accept-encoding': 'gzip, deflate, br', - 'content-length': '18055', - }, - }, + _vnote: '', state: 'submitted', form: '65ea368b705068f84a93c87a', @@ -1024,6 +969,8 @@ describe('Process Tests', () => { submission.data = context.data; context.processors = ProcessTargets.evaluator; processSync(context); + console.log(context.scope.errors); + assert.equal(context.scope.errors.length, 0); }); it('should remove submission data not in a nested form definition', async function () { @@ -2853,6 +2800,185 @@ describe('Process Tests', () => { expect((context.scope as ValidationScope).errors).to.have.length(1); }); }); + it('Should not return fields from conditionally hidden containers', async () => { + const form = { + display: 'form', + "components": [ + { + "title": "__information_on_the_appointee", + "theme": "primary", + "collapsible": false, + "key": "HeadingNestedFormCandidates", + "type": "panel", + "label": "Appointees", + "input": false, + "tableView": false, + "components": [ + { + "label": "Appointees", + "hideLabel": true, + "tableView": false, + + "addAnother": "__add_appointee", + "modal": true, + "saveRow": "Close", + "rowDrafts": true, + "key": "candidates", + "type": "editgrid", + "displayAsTable": false, + "input": true, + "components": [ + { + "label": "Appointee", + "tableView": false, + "key": "candidate", + "type": "container", + "input": true, + "components": [ + { + "label": "Data", + "tableView": false, + "key": "data", + "type": "container", + "input": true, + "components": [ + { + "label": "Tabs", + "components": [ + { + "label": "__6_time_commitment", + "key": "section6tab", + "components": [ + { + "label": "Section 6", + "tableView": false, + "clearOnHide": true, + "validateWhenHidden": false, + "key": "section6", + "properties": { + "clearHiddenOnSave": "true" + }, + "customConditional": "show = false;", + "type": "container", + "input": true, + "components": [ + { + "title": "__6_dash_time_commitment", + "theme": "primary", + "collapsible": false, + "key": "heading6", + "type": "panel", + "label": "Time Commitment", + "input": false, + "tableView": false, + "components": [ + { + "label": "__a_information_to_be_provided_by_the_supervised_entity_the", + "description": "__ul_li_see_the_report_on_declared_time_commitment_of", + "autoExpand": false, + "tableView": true, + "validate": { + "required": true + }, + "key": "entityExpectedTimeCommit", + "type": "textarea", + "input": true + }, + { + "label": "c", + "tableView": false, + "key": "c", + "type": "container", + "input": true, + "components": [] + }, + { + "label": "__d_list_of_executive_and_non_executive_directorships_and_other", + "description": "__for_each_directorship_or_other_activity_a_separate_row_needs", + "tableView": false, + "addAnother": "__add_another", + "validate": { + "required": true + }, + "rowDrafts": false, + "key": "d", + "type": "editgrid", + "input": true, + "components": [] + } + ] + } + ] + } + ] + } + ], + "key": "tabs1", + "type": "tabs", + "input": false, + "tableView": false + } + ] + } + ] + } + ] + } + ] + }, + { + "label": "Submit", + "action": "saveState", + "showValidations": false, + "tableView": false, + "key": "submit", + "type": "button", + "input": true, + "state": "draft" + } + ], + + }; + const submission = { + "data": { + "candidates": [ + { + "candidate": { + "data": { + "section6": { + "c": {}, + "d": [] + } + } + } + } + ], + "submit": true + } + }; + + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + processSync(context); + context.processors = ProcessTargets.evaluator; + processSync(context); + console.log(JSON.stringify(context.data, null, 2)) + expect(context.data).to.deep.equal({ + candidates:[{candidate:{data:{}}}], + submit: true + + }); + }) + describe('For EditGrid:', () => { const components = [ { @@ -2893,9 +3019,14 @@ describe('Process Tests', () => { form: { data: { textField: 'test', + invalidField: 'bad', }, + }, }, + { + invalidDataGridField: 'wrong', + }, ], }, }; @@ -2914,7 +3045,12 @@ describe('Process Tests', () => { processSync(context); context.processors = ProcessTargets.evaluator; processSync(context); + console.log(JSON.stringify(context.data, null, 2)); + expect((context.scope as ValidationScope).errors).to.have.length(0); + expect(context.data).to.deep.equal({ + editGrid: [{ form: { data: { textField: 'test' } } }], + }); }); it('Should not validate required component when it is not filled out', async () => { const submission = { From dd0ccf338ecb43c520348c6bf8008412226c194a Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Tue, 16 Jul 2024 11:18:07 -0500 Subject: [PATCH 3/4] Removed a non-implemented test. --- src/process/filter/__tests__/filter.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/process/filter/__tests__/filter.test.ts b/src/process/filter/__tests__/filter.test.ts index 71015bb6..dd225aed 100644 --- a/src/process/filter/__tests__/filter.test.ts +++ b/src/process/filter/__tests__/filter.test.ts @@ -71,7 +71,3 @@ it('Should not filter empty array value for datTable component', async () => { filterProcessSync(context); expect(context.scope.filter).to.deep.equal({'dataTable': {'compModelType': 'array', 'include': true, value: []}}); }); - -it('Should filter nested fields within a nested form correctly.', async () => { - -}); From 0b973afa3fd2a82eb38eed12b0406ad2eeff5a53 Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Tue, 16 Jul 2024 11:18:57 -0500 Subject: [PATCH 4/4] Adding a new line. --- src/process/filter/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process/filter/index.ts b/src/process/filter/index.ts index 4e022704..07df7558 100644 --- a/src/process/filter/index.ts +++ b/src/process/filter/index.ts @@ -72,4 +72,4 @@ export const filterProcessInfo: ProcessorInfo = { processSync: filterProcessSync, postProcess: filterPostProcess, shouldProcess: (context: FilterContext) => true, -}; \ No newline at end of file +};