From 07370d963f10b8b59fc33e914244b4c43c2f85c5 Mon Sep 17 00:00:00 2001 From: Wiktor Danielewski <63188869+wiktord2000@users.noreply.github.com> Date: Tue, 21 May 2024 15:25:28 +0200 Subject: [PATCH] AAE-22577 Improve DataTable widget to support single object reference (e.g. only 1st element from array) - Case 3 (#9690) * AAE-22577 Add single object path reference support * AAE-22577 Update * AAE-22577 Fix unit tests --- .../data-table/data-table.widget.spec.ts | 120 ++++++++++-------- .../widgets/data-table/data-table.widget.ts | 2 +- .../data-table-path-parser.helper.spec.ts | 24 +++- .../helpers/data-table-path-parser.helper.ts | 13 +- .../data-table-path-parser.helper.mock.ts | 2 +- .../mocks/data-table-widget.mock.ts | 5 +- 6 files changed, 104 insertions(+), 62 deletions(-) diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts index ce5d0c9276..7f10c52d95 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts @@ -196,18 +196,6 @@ describe('DataTableWidgetComponent', () => { assertData(mockCountryColumns, mockEuropeCountriesRows); }); - it('should NOT display error if form is in preview state', () => { - widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); - spyOn(formCloudService, 'getPreviewState').and.returnValue(true); - fixture.detectChanges(); - - const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); - const previewDataTable = getPreview(); - - expect(failedErrorMsgElement).toBeNull(); - expect(previewDataTable).toBeTruthy(); - }); - it('should NOT display data table with data source if form is in preview state', () => { widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable); spyOn(formCloudService, 'getPreviewState').and.returnValue(true); @@ -220,53 +208,85 @@ describe('DataTableWidgetComponent', () => { expect(dataTable).toBeNull(); }); - it('should display error if data source is not linked to every column', () => { - widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); - fixture.detectChanges(); + describe('should NOT display error message if', () => { + it('form is in preview state', () => { + widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); + spyOn(formCloudService, 'getPreviewState').and.returnValue(true); + fixture.detectChanges(); - checkDataTableErrorMessage(); - expect(widget.dataSource.getRows()).toEqual([]); + const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); + const previewDataTable = getPreview(); + + expect(failedErrorMsgElement).toBeNull(); + expect(previewDataTable).toBeTruthy(); + }); + + it('path points to single object with appropriate schema definition', () => { + widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.single-object' }, mockSchemaDefinition, [], []); + widget.field.value = mockJsonNestedResponseEuropeCountriesData; + fixture.detectChanges(); + + const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message')); + + assertData(mockCountryColumns, [mockEuropeCountriesRows[1]]); + expect(failedErrorMsgElement).toBeNull(); + }); }); - it('should display error if data source has invalid column structure', () => { - widget.field = getDataVariable(mockVariableConfig, mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); - fixture.detectChanges(); + describe('should display error message if', () => { + it('data source is NOT linked to every column', () => { + widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); + fixture.detectChanges(); - checkDataTableErrorMessage(); - expect(widget.dataSource.getRows()).toEqual([]); - }); + checkDataTableErrorMessage(); + expect(widget.dataSource.getRows()).toEqual([]); + }); - it('should display error if data source is not found', () => { - widget.field = getDataVariable({ variableName: 'not-found-data-source' }, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); - fixture.detectChanges(); + it('data source has invalid column structure', () => { + widget.field = getDataVariable(mockVariableConfig, mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData); + fixture.detectChanges(); - checkDataTableErrorMessage(); - expect(widget.dataSource).toBeUndefined(); - }); + checkDataTableErrorMessage(); + expect(widget.dataSource.getRows()).toEqual([]); + }); - it('should display error if path is incorrect', () => { - widget.field = getDataVariable( - { ...mockVariableConfig, optionsPath: 'wrong.path' }, - mockSchemaDefinition, - mockJsonNestedResponseFormVariable, - [] - ); - fixture.detectChanges(); + it('data source is NOT found', () => { + widget.field = getDataVariable( + { variableName: 'not-found-data-source' }, + mockSchemaDefinition, + [], + mockJsonFormVariableWithIncorrectData + ); + fixture.detectChanges(); - checkDataTableErrorMessage(); - expect(widget.dataSource).toBeUndefined(); - }); + checkDataTableErrorMessage(); + expect(widget.dataSource).toBeUndefined(); + }); - it('should display error if provided data by path is not an array', () => { - widget.field = getDataVariable( - { ...mockVariableConfig, optionsPath: 'response.no-array' }, - mockSchemaDefinition, - mockJsonNestedResponseFormVariable, - [] - ); - fixture.detectChanges(); + it('path is incorrect', () => { + widget.field = getDataVariable( + { ...mockVariableConfig, optionsPath: 'wrong.path' }, + mockSchemaDefinition, + mockJsonNestedResponseFormVariable, + [] + ); + fixture.detectChanges(); - checkDataTableErrorMessage(); - expect(widget.dataSource).toBeUndefined(); + checkDataTableErrorMessage(); + expect(widget.dataSource).toBeUndefined(); + }); + + it('provided data by path is NOT an array or object', () => { + widget.field = getDataVariable( + { ...mockVariableConfig, optionsPath: 'response.no-array-or-object' }, + mockSchemaDefinition, + mockJsonNestedResponseFormVariable, + [] + ); + fixture.detectChanges(); + + checkDataTableErrorMessage(); + expect(widget.dataSource).toBeUndefined(); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts index fe2c786e57..82861ec275 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.ts @@ -83,7 +83,7 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit this.handleError('Data source has corrupted model or structure'); } } else { - this.handleError('Data source not found or it is not an array'); + this.handleError('Data source not found or it is not an array/object'); } } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts index 7cda0f4ed2..9874ccc675 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts @@ -16,7 +16,7 @@ */ import { DataTablePathParserHelper } from './data-table-path-parser.helper'; -import { mockResponseResultData, mockResponseResultDataWithNestedArray, mockResultData } from '../mocks/data-table-path-parser.helper.mock'; +import { mockResponseResultData, mockResponseResultDataWithArrayInsideArray, mockResultData } from '../mocks/data-table-path-parser.helper.mock'; interface DataTablePathParserTestCase { description: string; @@ -132,20 +132,32 @@ describe('DataTablePathParserHelper', () => { description: 'with property followed by single index reference', propertyName: 'users', path: 'response.users[0].data', - data: mockResponseResultDataWithNestedArray('users') + data: mockResponseResultDataWithArrayInsideArray('users') }, { description: 'with property followed by multiple index references', propertyName: 'users:Array', path: 'response.[users:Array][0][1][12].data', - data: mockResponseResultDataWithNestedArray('users:Array'), + data: mockResponseResultDataWithArrayInsideArray('users:Array'), expected: [] }, { - description: 'when path does NOT point to array', + description: 'when path points to array in the middle (incorrect path)', propertyName: 'users', - path: 'response.users[0]', - data: mockResponseResultDataWithNestedArray('users'), + path: 'response.users.incorrectPath', + data: mockResponseResultDataWithArrayInsideArray('users'), + expected: [] + }, + { + description: 'when path points to the particular element of the array', + propertyName: 'users', + path: 'response.users[1]', + expected: [mockResultData[1]] + }, + { + description: 'when path points to the particular element of the array which does NOT exist', + propertyName: 'users', + path: 'response.users[100]', expected: [] } ]; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts index 05d5612a7e..91b0096fd6 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts @@ -37,8 +37,10 @@ export class DataTablePathParserHelper { const isPropertyWithSingleIndexReference = propertyIndexReferences.length === 1; const nestedData = isPropertyWithSingleIndexReference ? data[purePropertyName]?.[propertyIndexReferences[0]] : data[purePropertyName]; - if (Array.isArray(nestedData)) { - return nestedData; + if (nestedData && properties.length === 0) { + if (this.isDataArrayOrObject(nestedData)) { + return Array.isArray(nestedData) ? nestedData : [nestedData]; + } } return this.retrieveDataFromPath(nestedData, properties.join('.')); @@ -127,4 +129,11 @@ export class DataTablePathParserHelper { private isPropertyExistsInData(data: any, property: string): boolean { return Object.prototype.hasOwnProperty.call(data, property); } + + private isDataArrayOrObject(data: any): boolean { + if (data == null) { + return false; + } + return Array.isArray(data) || typeof data === 'object'; + } } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts index 1c955e987a..a36a343ccd 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts @@ -44,7 +44,7 @@ export const mockResponseResultData = (propertyName?: string) => ({ } }); -export const mockResponseResultDataWithNestedArray = (propertyName?: string) => ({ +export const mockResponseResultDataWithArrayInsideArray = (propertyName?: string) => ({ response: { [propertyName]: [ { diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts index 5d37f9fee1..0b5c69f3b2 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts @@ -114,13 +114,14 @@ export const mockJsonNestedResponseEuropeCountriesData = { response: { empty: [], 'my-data': mockEuropeCountriesData, + 'single-object': mockEuropeCountriesData[0], + 'no-array-or-object': 'string-value', data: [ { id: 'HR', name: 'Croatia' } - ], - 'no-array': {} + ] } };