Skip to content

Commit d145f2c

Browse files
authored
✨AI Column: Test unsupported Formatting properties (#31794)
1 parent fbc6f5e commit d145f2c

File tree

3 files changed

+176
-0
lines changed

3 files changed

+176
-0
lines changed

packages/devextreme/js/__internal/grids/grid_core/ai_column/__tests__/ai_column.integration.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,115 @@ describe('columnOption', () => {
847847
.toEqual(['ID', 'AI Column', 'Name', 'Value']);
848848
});
849849

850+
it('should apply encodeHtml to AI column', async () => {
851+
const aiIntegration = new AIIntegration({
852+
sendRequest(prompt): RequestResult {
853+
return {
854+
promise: new Promise<string>((resolve) => {
855+
const result = {};
856+
Object.entries(prompt.data?.data).forEach(([key]) => {
857+
result[key] = '<script>alert(\'XSS\')</script>';
858+
});
859+
resolve(JSON.stringify(result));
860+
}),
861+
abort: (): void => {},
862+
};
863+
},
864+
});
865+
const { component } = await createDataGrid({
866+
dataSource: [
867+
{ id: 1, name: 'Name 1', value: 10 },
868+
],
869+
columns: [
870+
{ dataField: 'id', caption: 'ID' },
871+
{
872+
type: 'ai',
873+
caption: 'AI Column',
874+
name: 'myColumn',
875+
ai: {
876+
aiIntegration,
877+
prompt: 'Initial Prompt',
878+
},
879+
},
880+
],
881+
});
882+
883+
await Promise.resolve();
884+
expect(component.getDataCell(0, 1).getText()).toBe('<script>alert(\'XSS\')</script>');
885+
886+
component.apiColumnOption('myColumn', 'encodeHtml', false);
887+
expect(component.getDataCell(0, 1).getText()).toBe('alert(\'XSS\')');
888+
expect(component.apiColumnOption('myColumn').encodeHtml).toBe(false);
889+
});
890+
891+
it('should use calculateDisplayValue for AI column', async () => {
892+
const aiIntegration = new AIIntegration({
893+
sendRequest(): RequestResult {
894+
return {
895+
promise: new Promise<string>((resolve) => {
896+
resolve('{"1":"AI Value"}');
897+
}),
898+
abort: (): void => {},
899+
};
900+
},
901+
});
902+
const { component } = await createDataGrid({
903+
dataSource: [
904+
{ id: 1, name: 'Name 1', value: 10 },
905+
],
906+
columns: [
907+
{ dataField: 'id', caption: 'ID' },
908+
{
909+
type: 'ai',
910+
caption: 'AI Column',
911+
name: 'myColumn',
912+
ai: {
913+
aiIntegration,
914+
prompt: 'Initial Prompt',
915+
},
916+
calculateDisplayValue: (): string => 'Calculated AI Value',
917+
},
918+
],
919+
});
920+
921+
await Promise.resolve();
922+
expect(component.getDataCell(0, 1).getText()).toBe('Calculated AI Value');
923+
});
924+
925+
it('should use customizeText for AI column', async () => {
926+
const aiIntegration = new AIIntegration({
927+
sendRequest(): RequestResult {
928+
return {
929+
promise: new Promise<string>((resolve) => {
930+
resolve('{"1":"AI Value"}');
931+
}),
932+
abort: (): void => {},
933+
};
934+
},
935+
});
936+
const { component } = await createDataGrid({
937+
dataSource: [
938+
{ id: 1, name: 'Name 1', value: 10 },
939+
],
940+
columns: [
941+
{ dataField: 'id', caption: 'ID' },
942+
{
943+
type: 'ai',
944+
caption: 'AI Column',
945+
name: 'myColumn',
946+
ai: {
947+
aiIntegration,
948+
prompt: 'Initial Prompt',
949+
},
950+
customizeText: (cellInfo): string => `Customized: ${cellInfo.valueText}`,
951+
},
952+
],
953+
});
954+
955+
await Promise.resolve();
956+
expect(component.getDataCell(0, 1).getText()).toBe('Customized: AI Value');
957+
});
958+
850959
describe('when the name is reset', () => {
851960
it('should throw E1066', async () => {
852961
const { component } = await createDataGrid({

packages/devextreme/js/__internal/grids/grid_core/ai_column/__tests__/unsupported_base_properties.integration.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {
22
afterEach, beforeEach, describe, expect, it, jest,
33
} from '@jest/globals';
4+
import type { GenerateGridColumnCommandResponse } from '@js/common/ai-integration';
45
import type { dxElementWrapper } from '@js/core/renderer';
56
import $ from '@js/core/renderer';
67
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
78
import DataGrid from '@js/ui/data_grid';
89
import errors from '@js/ui/widget/ui.errors';
10+
import { AIIntegration } from '@ts/core/ai_integration/core/ai_integration';
911
import { DataGridModel } from '@ts/grids/data_grid/__tests__/__mock__/model/data_grid';
1012

1113
const SELECTORS = {
@@ -20,6 +22,13 @@ const dataSource = [
2022
{ id: 3, name: 'Item 3', value: 3 },
2123
];
2224

25+
const EMPTY_CELL_TEXT = '\u00A0';
26+
27+
interface RequestResult {
28+
promise: Promise<GenerateGridColumnCommandResponse>;
29+
abort: () => void;
30+
}
31+
2332
const createDataGrid = async (
2433
options: DataGridProperties = {},
2534
): Promise<{
@@ -391,4 +400,61 @@ describe('Unsupported properties', () => {
391400
},
392401
);
393402
});
403+
404+
describe('Formatting properties', () => {
405+
it('Should not apply format to AI column (first load)', async () => {
406+
const aiIntegration = new AIIntegration({
407+
sendRequest(prompt): RequestResult {
408+
return {
409+
promise: new Promise<string>((resolve) => {
410+
const result = {};
411+
Object.entries(prompt.data?.data).forEach(([key, value]) => {
412+
const { id, name } = value as { id: number; name: string };
413+
if (id === 1) {
414+
result[key] = '';
415+
} else {
416+
result[key] = `Response ${name}`;
417+
}
418+
});
419+
resolve(JSON.stringify(result));
420+
}),
421+
abort: (): void => {},
422+
};
423+
},
424+
});
425+
const { component } = await createDataGrid({
426+
dataSource,
427+
showBorders: true,
428+
keyExpr: 'id',
429+
columns: [
430+
'id',
431+
{
432+
caption: 'AI',
433+
type: 'ai',
434+
name: 'AItest',
435+
ai: {
436+
prompt: 'Provide name for item with value {value}',
437+
aiIntegration,
438+
},
439+
format: {
440+
type: 'currency',
441+
precision: 0,
442+
currency: 'EUR',
443+
},
444+
trueText: 'Yes',
445+
falseText: 'No',
446+
buttons: ['edit', 'delete'],
447+
setCellValue: (rowData, value) => {
448+
rowData.name = value;
449+
},
450+
},
451+
],
452+
});
453+
454+
await Promise.resolve();
455+
456+
expect(component.getDataCell(0, 1).getText()).toBe(EMPTY_CELL_TEXT);
457+
expect(component.getDataCell(1, 1).getText()).toBe('Response Item 2');
458+
});
459+
});
394460
});

packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const getAICommandColumnDefaultOptions = (): object => ({
99
command: AI_COLUMN_NAME,
1010
cssClass: CLASSES.aiColumn,
1111
fixed: false,
12+
encodeHtml: true,
1213
minWidth: 120,
1314
});
1415

0 commit comments

Comments
 (0)