Skip to content

Commit 2ca1df7

Browse files
update tests and final fixes for drag and drop
1 parent 949a614 commit 2ca1df7

File tree

9 files changed

+436
-380
lines changed

9 files changed

+436
-380
lines changed
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import {
4+
calculateAdjustedRowIndex,
5+
insertModuleIntoRows,
6+
removeModuleFromRows,
7+
validateModuleMoveConstraints,
8+
} from '@app/homeV3/context/hooks/utils/moduleOperationsUtils';
9+
import { ModulePositionInput } from '@app/homeV3/template/types';
10+
11+
import { PageModuleFragment, PageTemplateFragment } from '@graphql/template.generated';
12+
import { DataHubPageModuleType, EntityType, PageModuleScope, PageTemplateScope, PageTemplateSurfaceType } from '@types';
13+
14+
// Mock data helpers
15+
const createMockModule = (name: string, urn: string): PageModuleFragment => ({
16+
urn,
17+
type: EntityType.DatahubPageModule,
18+
properties: {
19+
name,
20+
type: DataHubPageModuleType.OwnedAssets,
21+
visibility: { scope: PageModuleScope.Personal },
22+
params: {},
23+
},
24+
});
25+
26+
const createMockTemplate = (rows: any[]): PageTemplateFragment => ({
27+
urn: 'urn:li:pageTemplate:test',
28+
type: EntityType.DatahubPageTemplate,
29+
properties: {
30+
rows,
31+
surface: { surfaceType: PageTemplateSurfaceType.HomePage },
32+
visibility: { scope: PageTemplateScope.Personal },
33+
},
34+
});
35+
36+
describe('Module Operations Utility Functions', () => {
37+
describe('removeModuleFromRows', () => {
38+
const module1 = createMockModule('Module 1', 'urn:li:module:1');
39+
const module2 = createMockModule('Module 2', 'urn:li:module:2');
40+
const module3 = createMockModule('Module 3', 'urn:li:module:3');
41+
42+
it('should remove module and keep row when other modules exist', () => {
43+
const rows = [{ modules: [module1, module2, module3] }];
44+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 1 };
45+
46+
const result = removeModuleFromRows(rows, position);
47+
48+
expect(result.wasRowRemoved).toBe(false);
49+
expect(result.updatedRows).toHaveLength(1);
50+
expect(result.updatedRows[0].modules).toHaveLength(2);
51+
expect(result.updatedRows[0].modules).toEqual([module1, module3]);
52+
});
53+
54+
it('should remove entire row when removing last module', () => {
55+
const rows = [{ modules: [module1] }, { modules: [module2, module3] }];
56+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
57+
58+
const result = removeModuleFromRows(rows, position);
59+
60+
expect(result.wasRowRemoved).toBe(true);
61+
expect(result.updatedRows).toHaveLength(1);
62+
expect(result.updatedRows[0].modules).toEqual([module2, module3]);
63+
});
64+
65+
it('should handle invalid positions gracefully', () => {
66+
const rows = [{ modules: [module1] }];
67+
const position: ModulePositionInput = { rowIndex: 5, moduleIndex: 0 };
68+
69+
const result = removeModuleFromRows(rows, position);
70+
71+
expect(result.wasRowRemoved).toBe(false);
72+
expect(result.updatedRows).toEqual(rows);
73+
});
74+
75+
it('should handle undefined positions gracefully', () => {
76+
const rows = [{ modules: [module1] }];
77+
const position: ModulePositionInput = { rowIndex: undefined, moduleIndex: 0 };
78+
79+
const result = removeModuleFromRows(rows, position);
80+
81+
expect(result.wasRowRemoved).toBe(false);
82+
expect(result.updatedRows).toEqual(rows);
83+
});
84+
85+
it('should handle null rows gracefully', () => {
86+
const rows = null as any;
87+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
88+
89+
const result = removeModuleFromRows(rows, position);
90+
91+
expect(result.wasRowRemoved).toBe(false);
92+
expect(result.updatedRows).toEqual(rows);
93+
});
94+
});
95+
96+
describe('calculateAdjustedRowIndex', () => {
97+
it('should not adjust when no row was removed', () => {
98+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
99+
const toRowIndex = 2;
100+
const wasRowRemoved = false;
101+
102+
const result = calculateAdjustedRowIndex(fromPosition, toRowIndex, wasRowRemoved);
103+
104+
expect(result).toBe(2);
105+
});
106+
107+
it('should adjust when row was removed before target', () => {
108+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
109+
const toRowIndex = 2;
110+
const wasRowRemoved = true;
111+
112+
const result = calculateAdjustedRowIndex(fromPosition, toRowIndex, wasRowRemoved);
113+
114+
expect(result).toBe(1); // 2 - 1 = 1
115+
});
116+
117+
it('should not adjust when removed row is after target', () => {
118+
const fromPosition: ModulePositionInput = { rowIndex: 2, moduleIndex: 0 };
119+
const toRowIndex = 1;
120+
const wasRowRemoved = true;
121+
122+
const result = calculateAdjustedRowIndex(fromPosition, toRowIndex, wasRowRemoved);
123+
124+
expect(result).toBe(1); // No change
125+
});
126+
127+
it('should not adjust when removed row is same as target', () => {
128+
const fromPosition: ModulePositionInput = { rowIndex: 1, moduleIndex: 0 };
129+
const toRowIndex = 1;
130+
const wasRowRemoved = true;
131+
132+
const result = calculateAdjustedRowIndex(fromPosition, toRowIndex, wasRowRemoved);
133+
134+
expect(result).toBe(1); // No change
135+
});
136+
137+
it('should handle undefined fromPosition rowIndex gracefully', () => {
138+
const fromPosition: ModulePositionInput = { rowIndex: undefined, moduleIndex: 0 };
139+
const toRowIndex = 2;
140+
const wasRowRemoved = true;
141+
142+
const result = calculateAdjustedRowIndex(fromPosition, toRowIndex, wasRowRemoved);
143+
144+
expect(result).toBe(2); // No change when fromPosition is undefined
145+
});
146+
});
147+
148+
describe('insertModuleIntoRows', () => {
149+
const module1 = createMockModule('Module 1', 'urn:li:module:1');
150+
const module2 = createMockModule('Module 2', 'urn:li:module:2');
151+
const newModule = createMockModule('New Module', 'urn:li:module:new');
152+
153+
it('should insert new row when insertNewRow is true', () => {
154+
const rows = [{ modules: [module1] }];
155+
const position: ModulePositionInput = { rowIndex: 1, moduleIndex: 0 };
156+
157+
const result = insertModuleIntoRows(rows, newModule, position, 1, true);
158+
159+
expect(result).toHaveLength(2);
160+
expect(result[1].modules).toEqual([newModule]);
161+
expect(result[0].modules).toEqual([module1]); // Original row unchanged
162+
});
163+
164+
it('should append new row when target index exceeds rows length', () => {
165+
const rows = [{ modules: [module1] }];
166+
const position: ModulePositionInput = { rowIndex: 5, moduleIndex: 0 };
167+
168+
const result = insertModuleIntoRows(rows, newModule, position, 5, false);
169+
170+
expect(result).toHaveLength(2);
171+
expect(result[1].modules).toEqual([newModule]);
172+
});
173+
174+
it('should insert into existing row at specific position', () => {
175+
const rows = [{ modules: [module1, module2] }];
176+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 1 };
177+
178+
const result = insertModuleIntoRows(rows, newModule, position, 0, false);
179+
180+
expect(result[0].modules).toHaveLength(3);
181+
expect(result[0].modules[1]).toEqual(newModule);
182+
expect(result[0].modules[0]).toEqual(module1);
183+
expect(result[0].modules[2]).toEqual(module2);
184+
});
185+
186+
it('should append to existing row when moduleIndex is undefined', () => {
187+
const rows = [{ modules: [module1, module2] }];
188+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: undefined };
189+
190+
const result = insertModuleIntoRows(rows, newModule, position, 0, false);
191+
192+
expect(result[0].modules).toHaveLength(3);
193+
expect(result[0].modules[2]).toEqual(newModule); // Added at end
194+
});
195+
196+
it('should handle empty rows array', () => {
197+
const rows: any[] = [];
198+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
199+
200+
const result = insertModuleIntoRows(rows, newModule, position, 0, false);
201+
202+
expect(result).toHaveLength(1);
203+
expect(result[0].modules).toEqual([newModule]);
204+
});
205+
206+
it('should handle null rows gracefully', () => {
207+
const rows = null as any;
208+
const position: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
209+
210+
const result = insertModuleIntoRows(rows, newModule, position, 0, false);
211+
212+
expect(result).toHaveLength(1);
213+
expect(result[0].modules).toEqual([newModule]);
214+
});
215+
});
216+
217+
describe('validateModuleMoveConstraints', () => {
218+
const module1 = createMockModule('Module 1', 'urn:li:module:1');
219+
const module2 = createMockModule('Module 2', 'urn:li:module:2');
220+
const module3 = createMockModule('Module 3', 'urn:li:module:3');
221+
222+
it('should allow move when target row has space', () => {
223+
const template = createMockTemplate([
224+
{ modules: [module1, module2] }, // Only 2 modules
225+
]);
226+
const fromPosition: ModulePositionInput = { rowIndex: 1, moduleIndex: 0 };
227+
const toPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 2 };
228+
229+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
230+
231+
expect(result).toBeNull();
232+
});
233+
234+
it('should prevent move when target row is full and dragging from different row', () => {
235+
const template = createMockTemplate([
236+
{ modules: [module1, module2, module3] }, // Full row (3 modules)
237+
]);
238+
const fromPosition: ModulePositionInput = { rowIndex: 1, moduleIndex: 0 };
239+
const toPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 3 };
240+
241+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
242+
243+
expect(result).toBe('Cannot move module: Target row already has maximum number of modules');
244+
});
245+
246+
it('should allow move within same row even when full', () => {
247+
const template = createMockTemplate([
248+
{ modules: [module1, module2, module3] }, // Full row
249+
]);
250+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
251+
const toPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 2 };
252+
253+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
254+
255+
expect(result).toBeNull();
256+
});
257+
258+
it('should handle missing template data gracefully', () => {
259+
const template = createMockTemplate([]);
260+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
261+
const toPosition: ModulePositionInput = { rowIndex: 1, moduleIndex: 0 };
262+
263+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
264+
265+
expect(result).toBeNull();
266+
});
267+
268+
it('should handle undefined toPosition rowIndex gracefully', () => {
269+
const template = createMockTemplate([{ modules: [module1, module2, module3] }]);
270+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
271+
const toPosition: ModulePositionInput = { rowIndex: undefined, moduleIndex: 0 };
272+
273+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
274+
275+
expect(result).toBeNull();
276+
});
277+
278+
it('should handle missing template properties gracefully', () => {
279+
const template = {
280+
urn: 'urn:li:pageTemplate:test',
281+
type: EntityType.DatahubPageTemplate,
282+
properties: null,
283+
} as any;
284+
const fromPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
285+
const toPosition: ModulePositionInput = { rowIndex: 0, moduleIndex: 0 };
286+
287+
const result = validateModuleMoveConstraints(template, fromPosition, toPosition);
288+
289+
expect(result).toBeNull();
290+
});
291+
});
292+
});

datahub-web-react/src/app/homeV3/context/hooks/__tests__/useDragAndDrop.test.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,6 @@ const mockModule1 = {
2525
},
2626
};
2727

28-
const mockModule2 = {
29-
urn: 'urn:li:pageModule:2',
30-
type: EntityType.DatahubPageModule,
31-
properties: {
32-
name: 'Module 2',
33-
type: DataHubPageModuleType.Domains,
34-
visibility: { scope: PageModuleScope.Personal },
35-
params: {},
36-
},
37-
};
38-
39-
const mockTemplate = {
40-
urn: 'urn:li:pageTemplate:test',
41-
type: EntityType.DatahubPageTemplate,
42-
properties: {
43-
rows: [
44-
{
45-
modules: [mockModule1, mockModule2],
46-
},
47-
],
48-
},
49-
};
50-
5128
describe('useDragAndDrop', () => {
5229
const mockMoveModule = vi.fn();
5330

@@ -62,7 +39,7 @@ describe('useDragAndDrop', () => {
6239
describe('activeModule state management', () => {
6340
it('should initialize with null activeModule', () => {
6441
const { result } = renderHook(() => useDragAndDrop());
65-
42+
6643
expect(result.current.activeModule).toBeNull();
6744
});
6845

datahub-web-react/src/app/homeV3/context/hooks/__tests__/useModuleOperations.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ describe('useModuleOperations', () => {
790790
const createModuleInput = {
791791
name: 'Test Module',
792792
type: DataHubPageModuleType.Link,
793+
scope: PageModuleScope.Personal,
793794
position,
794795
};
795796

@@ -846,6 +847,7 @@ describe('useModuleOperations', () => {
846847
const createModuleInput = {
847848
name: 'Test Module',
848849
type: DataHubPageModuleType.Link,
850+
scope: PageModuleScope.Personal,
849851
position,
850852
};
851853

0 commit comments

Comments
 (0)