Skip to content

Commit 32f9b5d

Browse files
committed
fix test and refactor code a bit
1 parent 89aed94 commit 32f9b5d

3 files changed

Lines changed: 46 additions & 225 deletions

File tree

packages/devextreme/js/__internal/grids/data_grid/__tests__/__mock__/model/data_grid.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import type { Column } from '@js/ui/data_grid';
22
import DataGrid from '@js/ui/data_grid';
33
import { DataGridBaseModel } from '@ts/grids/grid_core/__tests__/__mock__/model/data_grid_base';
44

5-
const SELECTORS = {
6-
summaryItem: 'dx-datagrid-summary-item',
7-
groupFooter: 'dx-datagrid-group-footer',
8-
footerRow: 'dx-footer-row',
9-
};
10-
115
export class DataGridModel extends DataGridBaseModel<DataGrid> {
126
protected NAME = 'dxDataGrid';
137

@@ -41,16 +35,4 @@ export class DataGridModel extends DataGridBaseModel<DataGrid> {
4135
instance.columnOption(columnName, optionName, optionValue);
4236
});
4337
}
44-
45-
public getFooterRow(): HTMLElement | null {
46-
return this.root.querySelector(`.${SELECTORS.footerRow}`);
47-
}
48-
49-
public getGroupFooterRows(): NodeListOf<HTMLElement> {
50-
return this.root.querySelectorAll(`.${SELECTORS.groupFooter}`);
51-
}
52-
53-
public getSummaryItems(row: HTMLElement): NodeListOf<HTMLElement> {
54-
return row.querySelectorAll(`.${SELECTORS.summaryItem}`);
55-
}
5638
}

packages/devextreme/js/__internal/grids/data_grid/summary/__tests__/m_summary.integration.test.ts

Lines changed: 30 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -6,232 +6,68 @@ import {
66
afterTest,
77
beforeTest,
88
createDataGrid,
9+
flushAsync,
910
} from '../../../grid_core/__tests__/__mock__/helpers/utils';
1011

1112
describe('Summary', () => {
1213
beforeEach(beforeTest);
1314
afterEach(afterTest);
1415

15-
describe('column lookup map performance optimization', () => {
16+
describe('column lookup map performance optimization (T1316562)', () => {
17+
const SUMMARY_COUNT = 100;
18+
const GROUP_COUNT = 4;
19+
1620
const dataSource = [
1721
{
18-
id: 1, name: 'Alice', value: 10, category: 'A',
22+
id: 1, name: 'Alice', value: 10, category: 'A', region: 'X',
1923
},
2024
{
21-
id: 2, name: 'Bob', value: 20, category: 'A',
25+
id: 2, name: 'Bob', value: 20, category: 'A', region: 'Y',
2226
},
2327
{
24-
id: 3, name: 'Carol', value: 30, category: 'B',
28+
id: 3, name: 'Carol', value: 30, category: 'B', region: 'X',
2529
},
2630
{
27-
id: 4, name: 'Dave', value: 40, category: 'B',
31+
id: 4, name: 'Dave', value: 40, category: 'B', region: 'Y',
2832
},
2933
];
3034

31-
it('should correctly calculate total summary', async () => {
35+
const groupSummaryItems = Array.from(
36+
{ length: SUMMARY_COUNT },
37+
(_, i) => ({
38+
column: i % 2 === 0 ? 'value' : 'id',
39+
summaryType: 'sum' as const,
40+
showInGroupFooter: false,
41+
name: `summary_${i}`,
42+
}),
43+
);
44+
45+
it('should use columnMap optimization and avoid O(n*m) columnOption calls on refresh', async () => {
3246
const { instance } = await createDataGrid({
33-
dataSource,
34-
columns: ['id', 'name', 'value', 'category'],
35-
summary: {
36-
totalItems: [
37-
{ column: 'value', summaryType: 'sum' },
38-
{ column: 'value', summaryType: 'avg' },
39-
{ column: 'id', summaryType: 'count' },
40-
],
41-
},
42-
});
43-
44-
jest.runAllTimers();
45-
46-
expect(instance.getTotalSummaryValue('sum_value')).toBe(100);
47-
expect(instance.getTotalSummaryValue('avg_value')).toBe(25);
48-
expect(instance.getTotalSummaryValue('count_id')).toBe(4);
49-
});
50-
51-
it('should render total footer with summary items', async () => {
52-
const { component } = await createDataGrid({
53-
dataSource,
54-
columns: ['id', 'name', 'value', 'category'],
55-
summary: {
56-
totalItems: [
57-
{ column: 'value', summaryType: 'sum' },
58-
],
59-
},
60-
});
61-
62-
jest.runAllTimers();
63-
64-
const footerRow = component.getFooterRow() as HTMLElement;
65-
66-
expect(footerRow).not.toBeNull();
67-
68-
const summaryItems = component.getSummaryItems(footerRow);
69-
const summary = dataSource.reduce((acc, item) => acc + item.value, 0);
70-
71-
expect(summaryItems.length).toBe(1);
72-
expect(summaryItems[0].textContent).toContain(summary.toString());
73-
});
74-
75-
it('should calculate group summary with many groupItems', async () => {
76-
const { component } = await createDataGrid({
77-
dataSource,
78-
columns: [
79-
{ dataField: 'id' },
80-
{ dataField: 'name' },
81-
{ dataField: 'value' },
82-
{ dataField: 'category', groupIndex: 0 },
83-
],
84-
summary: {
85-
groupItems: [
86-
{ column: 'value', summaryType: 'sum', showInGroupFooter: false },
87-
{ column: 'value', summaryType: 'avg', showInGroupFooter: false },
88-
{ column: 'id', summaryType: 'count', showInGroupFooter: false },
89-
],
90-
},
91-
});
92-
93-
jest.runAllTimers();
94-
95-
const groupRows = component.getGroupRows();
96-
97-
expect(groupRows.length).toBe(2);
98-
99-
// Group summary items with showInGroupFooter: false
100-
// are rendered inline in the group row cell text
101-
const firstGroupRowText = groupRows[0].textContent ?? '';
102-
103-
expect(firstGroupRowText).toContain('Sum');
104-
expect(firstGroupRowText).toContain('Avg');
105-
expect(firstGroupRowText).toContain('Count');
106-
});
107-
108-
it('should render group footer summary', async () => {
109-
const { component } = await createDataGrid({
110-
dataSource,
111-
columns: [
112-
{ dataField: 'id' },
113-
{ dataField: 'name' },
114-
{ dataField: 'value' },
115-
{ dataField: 'category', groupIndex: 0 },
116-
],
117-
summary: {
118-
groupItems: [
119-
{
120-
column: 'value',
121-
summaryType: 'sum',
122-
showInGroupFooter: true,
123-
},
124-
],
125-
},
126-
});
127-
128-
jest.runAllTimers();
129-
130-
const groupFooterRows = component.getGroupFooterRows();
131-
132-
expect(groupFooterRows.length).toBe(2);
133-
134-
const summaryItems = component.getSummaryItems(
135-
groupFooterRows[0],
136-
);
137-
138-
expect(summaryItems.length).toBe(1);
139-
expect(summaryItems[0].textContent).toContain('30');
140-
});
141-
142-
it('should correctly calculate summary with showInColumn option', async () => {
143-
const { component } = await createDataGrid({
144-
dataSource,
145-
columns: [
146-
{ dataField: 'id' },
147-
{ dataField: 'name' },
148-
{ dataField: 'value' },
149-
{ dataField: 'category', groupIndex: 0 },
150-
],
151-
summary: {
152-
groupItems: [
153-
{
154-
column: 'value',
155-
summaryType: 'sum',
156-
showInColumn: 'name',
157-
showInGroupFooter: false,
158-
},
159-
],
160-
},
161-
});
162-
163-
jest.runAllTimers();
164-
165-
const groupRows = component.getGroupRows();
166-
167-
expect(groupRows.length).toBe(2);
168-
});
169-
170-
it('should handle many summary items without errors', async () => {
171-
const groupItems = Array.from(
172-
{ length: 50 },
173-
(_, i) => ({
174-
column: i % 2 === 0 ? 'value' : 'id',
175-
summaryType: 'sum' as const,
176-
showInGroupFooter: false,
177-
name: `summary_${i}`,
178-
}),
179-
);
180-
181-
const { component } = await createDataGrid({
182-
dataSource,
183-
columns: [
184-
{ dataField: 'id' },
185-
{ dataField: 'name' },
186-
{ dataField: 'value' },
187-
{ dataField: 'category', groupIndex: 0 },
188-
],
189-
summary: {
190-
groupItems,
191-
},
192-
});
193-
194-
jest.runAllTimers();
195-
196-
const groupRows = component.getGroupRows();
197-
198-
expect(groupRows.length).toBe(2);
199-
});
200-
201-
it('should handle combined total and group summary', async () => {
202-
const { instance, component } = await createDataGrid({
20347
dataSource,
20448
columns: [
20549
{ dataField: 'id' },
20650
{ dataField: 'name' },
20751
{ dataField: 'value' },
20852
{ dataField: 'category', groupIndex: 0 },
53+
{ dataField: 'region', groupIndex: 1 },
20954
],
210-
summary: {
211-
totalItems: [
212-
{ column: 'value', summaryType: 'sum' },
213-
],
214-
groupItems: [
215-
{
216-
column: 'value',
217-
summaryType: 'sum',
218-
showInGroupFooter: false,
219-
},
220-
],
221-
},
55+
summary: { groupItems: groupSummaryItems },
22256
});
22357

224-
jest.runAllTimers();
58+
await flushAsync();
22559

226-
expect(instance.getTotalSummaryValue('sum_value')).toBe(100);
60+
const columnsController = instance.getController('columns');
61+
const spy = jest.spyOn(columnsController, 'columnOption');
22762

228-
const footerRow = component.getFooterRow();
63+
instance.refresh().catch(() => {});
64+
await flushAsync();
22965

230-
expect(footerRow).not.toBeNull();
66+
const worstCaseMinCalls = SUMMARY_COUNT * GROUP_COUNT;
23167

232-
const groupRows = component.getGroupRows();
68+
expect(spy.mock.calls.length).toBeLessThan(worstCaseMinCalls);
23369

234-
expect(groupRows.length).toBe(2);
70+
spy.mockRestore();
23571
});
23672
});
23773
});

packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -534,20 +534,24 @@ const data = (Base: ModuleType<DataController>) => class SummaryDataControllerEx
534534
// The map is built once per _processItems cycle (via options) and discarded after.
535535
private _buildColumnLookupMap(): Map<string | number, Column> {
536536
const columnMap = new Map<string | number, Column>();
537-
const { _columnsController: ctrl } = this;
538-
const allColumns = ctrl.getColumns()
539-
.concat(ctrl._commandColumns ?? []);
537+
const allColumns = [
538+
...this._columnsController.getColumns(),
539+
...(this._columnsController._commandColumns ?? []),
540+
];
540541

541542
for (const column of allColumns) {
542-
const copy = extend({}, column) as Column;
543+
const copiedColumn = { ...column };
543544
const keys = [
544-
column.index, column.name,
545-
column.dataField, column.caption,
546-
];
545+
column.index,
546+
column.name,
547+
column.dataField,
548+
column.caption,
549+
].filter((key) => (
550+
key !== undefined && !columnMap.has(key)
551+
));
552+
547553
for (const key of keys) {
548-
if (key !== undefined && !columnMap.has(key)) {
549-
columnMap.set(key, copy);
550-
}
554+
columnMap.set(key, copiedColumn);
551555
}
552556
}
553557

@@ -562,18 +566,17 @@ const data = (Base: ModuleType<DataController>) => class SummaryDataControllerEx
562566
isGroupRow?,
563567
columnMap?: Map<string | number, Column>,
564568
) {
565-
const that = this;
566569
const summaryCells: any = [];
567570
const summaryCellsByColumns = {};
568571

569572
each(summaryItems, (summaryIndex, summaryItem) => {
570573
const column = columnMap
571574
? getColumnFromMap(summaryItem.column, columnMap)
572-
: that._columnsController.columnOption(summaryItem.column);
575+
: this._columnsController.columnOption(summaryItem.column);
573576
const showInColumn = (summaryItem.showInColumn
574577
&& (columnMap
575578
? getColumnFromMap(summaryItem.showInColumn, columnMap)
576-
: that._columnsController.columnOption(summaryItem.showInColumn)))
579+
: this._columnsController.columnOption(summaryItem.showInColumn)))
577580
|| column;
578581
const columnIndex = calculateTargetColumnIndex(summaryItem, showInColumn);
579582

0 commit comments

Comments
 (0)