diff --git a/e2e/testcafe-devextreme/tests/common/treeList/aiColumn/functional.ts b/e2e/testcafe-devextreme/tests/common/treeList/aiColumn/functional.ts new file mode 100644 index 000000000000..d35623c3eb20 --- /dev/null +++ b/e2e/testcafe-devextreme/tests/common/treeList/aiColumn/functional.ts @@ -0,0 +1,505 @@ +import TreeList from 'devextreme-testcafe-models/treeList'; +import url from '../../../../helpers/getPageUrl'; +import { createWidget } from '../../../../helpers/createWidget'; + +fixture`Ai Column.Common (TreeList)` + .page(url(__dirname, '../../../container-ai-integration.html')); + +const TREE_LIST_SELECTOR = '#container'; +const EMPTY_CELL_TEXT = '\u00A0'; +const DROPDOWNMENU_PROMPT_EDITOR_INDEX = 0; +const DROPDOWNMENU_REGENERATE_INDEX = 1; +const DROPDOWNMENU_CLEAR_DATA_INDEX = 2; + +test('Get result from AI and display it in the AI column', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Get result from AI and display it in two AI columns', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + await t.expect(treeList.getDataCell(0, 4).element.innerText).eql('Response Name 1 for second AI column'); + await t.expect(treeList.getDataCell(1, 4).element.innerText).eql('Response Name 2 for second AI column'); + await t.expect(treeList.getDataCell(2, 4).element.innerText).eql('Response Name 3 for second AI column'); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + { + type: 'ai', + caption: 'AI Column2', + name: 'AI Column2', + ai: { + prompt: 'second AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Regenerate the AI request from DropDownButton menu', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + + const aiColumnHeaderCell = treeList.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_REGENERATE_INDEX).element); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + mode: 'manual', + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Regenerate the AI request from Prompt Editor', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + const aiColumnHeaderCell = treeList.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = treeList.getAIPromptEditor(); + + await t + .click(promptEditor.getRegenerateButton().element); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + mode: 'manual', + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Clear Data from AI column by DropDownButton menu', async (t) => { + // arrange, act + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + // assert + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + const aiColumnHeaderCell = treeList.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_CLEAR_DATA_INDEX).element); + + // assert + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Abort the AI request from Prompt Editor', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + + const aiColumnHeaderCell = treeList.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = treeList.getAIPromptEditor(); + + await t + .click(promptEditor.getRegenerateButton().element) + .click(promptEditor.getStopButton().element); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + mode: 'manual', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + setTimeout(() => { + resolve(JSON.stringify(result)); + }, 3000); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Change the prompt in the AI Prompt Editor', async (t) => { + const treeList = new TreeList(TREE_LIST_SELECTOR); + + await t.expect(treeList.isReady()).ok(); + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + + const aiColumnHeaderCell = treeList.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = treeList.getAIPromptEditor(); + + await t + .typeText(promptEditor.getTextArea().element, 'changed prompt', { replace: true }) + .click(promptEditor.getApplyButton().element); + + await t.expect(treeList.getDataCell(0, 3).element.innerText).eql('Response Name 1 for changed prompt'); + await t.expect(treeList.getDataCell(1, 3).element.innerText).eql('Response Name 2 for changed prompt'); + await t.expect(treeList.getDataCell(2, 3).element.innerText).eql('Response Name 3 for changed prompt'); +}).before(async () => createWidget('dxTreeList', () => ({ + dataSource: [ + { + id: 1, parentId: 0, name: 'Name 1', value: 10, + }, + { + id: 2, parentId: 1, name: 'Name 2', value: 20, + }, + { + id: 3, parentId: 1, name: 'Name 3', value: 30, + }, + ], + keyExpr: 'id', + parentIdExpr: 'parentId', + autoExpandAll: true, + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); diff --git a/e2e/testcafe-devextreme/tests/container-ai-integration.html b/e2e/testcafe-devextreme/tests/container-ai-integration.html new file mode 100644 index 000000000000..aa112a3ad9fb --- /dev/null +++ b/e2e/testcafe-devextreme/tests/container-ai-integration.html @@ -0,0 +1,33 @@ + + + + TestCafe Tests Container + + + + + + + + + + + + + + + + +
+

Test header

+ +
+
+
+
+
+ + diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/adaptivity.functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/adaptivity.functional.ts index 4fbf28edb814..863f6d88a78c 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/adaptivity.functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/adaptivity.functional.ts @@ -4,7 +4,7 @@ import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; fixture`Ai Column.Adaptivity` - .page(url(__dirname, './pages/containerWithAIIntegration.html')); + .page(url(__dirname, '../../../container-ai-integration.html')); const DATA_GRID_SELECTOR = '#container'; diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/functional.ts index 8f5a088e5a69..d6a7ac4ed2fb 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/functional.ts @@ -2,10 +2,14 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; -fixture.disablePageReloads`Ai Column.Common` - .page(url(__dirname, '../../../container.html')); +fixture`Ai Column.Common` + .page(url(__dirname, '../../../container-ai-integration.html')); const DATA_GRID_SELECTOR = '#container'; +const EMPTY_CELL_TEXT = '\u00A0'; +const DROPDOWNMENU_PROMPT_EDITOR_INDEX = 0; +const DROPDOWNMENU_REGENERATE_INDEX = 1; +const DROPDOWNMENU_CLEAR_DATA_INDEX = 2; test('The AI column with a given width', async (t) => { // arrange, act @@ -61,3 +65,440 @@ test('The AI column with a given min-width', async (t) => { }, ], })); + +test('Get result from AI and display it in the AI column', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Get result from AI and display it in two AI columns', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + await t.expect(dataGrid.getDataCell(0, 4).element.innerText).eql('Response Name 1 for second AI column'); + await t.expect(dataGrid.getDataCell(1, 4).element.innerText).eql('Response Name 2 for second AI column'); + await t.expect(dataGrid.getDataCell(2, 4).element.innerText).eql('Response Name 3 for second AI column'); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + { + type: 'ai', + caption: 'AI Column2', + name: 'AI Column2', + ai: { + prompt: 'second AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Regenerate the AI request from DropDownButton menu', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + + const aiColumnHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_REGENERATE_INDEX).element); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + mode: 'manual', + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Regenerate the AI request from Prompt Editor', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + const aiColumnHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = dataGrid.getAIPromptEditor(); + + await t + .click(promptEditor.getRegenerateButton().element); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + mode: 'manual', + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Clear Data from AI column by DropDownButton menu', async (t) => { + // arrange, act + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + // assert + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + const aiColumnHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_CLEAR_DATA_INDEX).element); + + // assert + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Abort the AI request from Prompt Editor', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); + + const aiColumnHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = dataGrid.getAIPromptEditor(); + + await t + .click(promptEditor.getRegenerateButton().element) + .click(promptEditor.getStopButton().element); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql(EMPTY_CELL_TEXT); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql(EMPTY_CELL_TEXT); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + mode: 'manual', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + setTimeout(() => { + resolve(JSON.stringify(result)); + }, 3000); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); + +test('Change the prompt in the AI Prompt Editor', async (t) => { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for first AI column'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for first AI column'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for first AI column'); + + const aiColumnHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(3); + + await t + .click(aiColumnHeaderCell.getAIHeaderButton().element); + const dropDownList = await aiColumnHeaderCell.getAIHeaderButton().getList(); + await t + .click(dropDownList.getItem(DROPDOWNMENU_PROMPT_EDITOR_INDEX).element); + + const promptEditor = dataGrid.getAIPromptEditor(); + + await t + .typeText(promptEditor.getTextArea().element, 'changed prompt', { replace: true }) + .click(promptEditor.getApplyButton().element); + + await t.expect(dataGrid.getDataCell(0, 3).element.innerText).eql('Response Name 1 for changed prompt'); + await t.expect(dataGrid.getDataCell(1, 3).element.innerText).eql('Response Name 2 for changed prompt'); + await t.expect(dataGrid.getDataCell(2, 3).element.innerText).eql('Response Name 3 for changed prompt'); +}).before(async () => createWidget('dxDataGrid', () => ({ + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + keyExpr: 'id', + columns: [ + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + { + type: 'ai', + caption: 'AI Column', + name: 'AI Column', + ai: { + prompt: 'first AI column', + // eslint-disable-next-line new-cap + aiIntegration: new (window as any).DevExpress.aiIntegration({ + sendRequest(prompt) { + return { + promise: new Promise((resolve) => { + const result: Record = {}; + + Object.entries(prompt.data?.data).forEach(([key, value]) => { + result[key] = `Response ${(value as any).name} for ${prompt.data?.text}`; + }); + + resolve(JSON.stringify(result)); + }), + abort: (): void => {}, + }; + }, + }), + }, + }, + ], +}))); diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/pages/containerWithAIIntegration.html b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/pages/containerWithAIIntegration.html deleted file mode 100644 index a60d11d7b061..000000000000 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/pages/containerWithAIIntegration.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - TestCafe Tests Container - - - - - - - - - - - - - - - - -
-

Test header

- -
-
-
-
-
- - diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/virtualScrolling.functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/virtualScrolling.functional.ts index 05792231c154..f4d9c6a3279f 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/virtualScrolling.functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/virtualScrolling.functional.ts @@ -4,7 +4,7 @@ import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; fixture`Ai Column.Virtual Scrolling.Functional` - .page(url(__dirname, './pages/containerWithAIIntegration.html')); + .page(url(__dirname, '../../../container-ai-integration.html')); const DATA_GRID_SELECTOR = '#container'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_integration_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_integration_controller.ts index cdb7bbd03bbb..6308a13e038e 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_integration_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_integration_controller.ts @@ -55,17 +55,9 @@ export class AIColumnIntegrationController extends Controller { cachedResponse: Record, callBacks?: RequestCallbacks, ): RequestCallbacks { - const column = this.columnsController.getColumnByName(columnName); const callbacks = { onComplete: (finalResponse: GenerateGridColumnCommandResult): void => { if (this.isRequestAwaitingCompletion(columnName)) { - const args = { - column, - error: null, - data: finalResponse.data, - }; - - this.executeAction('onAIColumnResponseReceived', args); this.aiColumnCacheController.setCachedResponse(columnName, finalResponse.data); this.processCommandCompletion(columnName); callBacks?.onComplete?.(finalResponse); @@ -73,11 +65,6 @@ export class AIColumnIntegrationController extends Controller { }, onError: (error: Error): void => { const message = error?.message ?? error; - this.executeAction('onAIColumnResponseReceived', { - column, - error: message, - data: null, - }); this.showError(message); this.processCommandCompletion(columnName); callBacks?.onError?.(error); @@ -96,7 +83,6 @@ export class AIColumnIntegrationController extends Controller { this.aiColumnCacheController.init(); this.createAction('onAIColumnRequestCreating'); - this.createAction('onAIColumnResponseReceived'); } public sendRequestCore({ diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts index d5e534052f7e..1ef7d9b84edb 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts @@ -196,13 +196,4 @@ export const columnHeadersViewExtender = ( super.renderDragCellContent($dragContainer, column); } - - public dispose(): void { - super.dispose(); - this.activeDropDownButtonInstance = null; - - if (this.aiColumnOptionChangedHandler) { - this._columnsController.aiColumnOptionChanged.remove(this.aiColumnOptionChangedHandler); - } - } }; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts index 493894fcedde..1ff42da2aac7 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts @@ -157,12 +157,4 @@ export class AIPromptEditorView extends View { // TODO: this.component.refresh(); } } - - public dispose(): void { - super.dispose(); - - if (this.aiColumnOptionChangedHandler) { - this.columnsController.aiColumnOptionChanged.remove(this.aiColumnOptionChangedHandler); - } - } } diff --git a/packages/testcafe-models/dataGrid/aiPromptEditor.ts b/packages/testcafe-models/dataGrid/aiPromptEditor.ts index 1b2342c40efe..b7c80c051fc5 100644 --- a/packages/testcafe-models/dataGrid/aiPromptEditor.ts +++ b/packages/testcafe-models/dataGrid/aiPromptEditor.ts @@ -1,4 +1,29 @@ import Popup from '../popup'; +import Button from '../button'; +import TextArea from '../textArea'; + +const CLASS = { + button: 'dx-button', + textArea: 'dx-ai-prompt-editor__text-area', + regenerateButton: 'dx-ai-prompt-editor__refresh-button', + stopButton: 'dx-ai-prompt-editor__stop-button', + applyButton: 'dx-ai-prompt-editor__apply-button', +}; export class AIPromptEditor extends Popup { + getTextArea(): TextArea { + return new TextArea(this.getWrapper().find(`.${CLASS.textArea}`)); + } + + getRegenerateButton(): Button { + return new Button(this.getWrapper().find(`.${CLASS.button}.${CLASS.regenerateButton}`)); + } + + getStopButton(): Button { + return new Button(this.getWrapper().find(`.${CLASS.button}.${CLASS.stopButton}`)); + } + + getApplyButton(): Button { + return new Button(this.getWrapper().find(`.${CLASS.button}.${CLASS.applyButton}`)); + } } diff --git a/packages/testcafe-models/dataGrid/headers/cell.ts b/packages/testcafe-models/dataGrid/headers/cell.ts index 4ac46b86c6a5..2ba6e9f6c867 100644 --- a/packages/testcafe-models/dataGrid/headers/cell.ts +++ b/packages/testcafe-models/dataGrid/headers/cell.ts @@ -1,6 +1,7 @@ import { ClientFunction, Selector } from 'testcafe'; import FocusableElement from '../../internal/focusable'; import Widget from '../../internal/widget'; +import DropDownButton from '../../dropDownButton'; type StickyPosition = 'left' | 'right' | 'sticky'; @@ -64,4 +65,8 @@ export default class HeaderCell { getEditor(): FocusableElement { return new FocusableElement(this.element.find('.dx-texteditor-input, .dx-checkbox')); } + + getAIHeaderButton(): DropDownButton { + return new DropDownButton(this.element.find(`.${CLASS.aiHeaderButton}`)); + } }