Skip to content

Commit 410075d

Browse files
authored
Merge pull request #3853 from aashu2006/test-files-delete
Add tests for file deletion in files reducer
2 parents 7cd2cae + d5635df commit 410075d

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import files from './files';
2+
import * as ActionTypes from '../../../constants';
3+
4+
// Helper to build test state without dispatching actions
5+
// Keeps DELETE_FILE tests isolated
6+
function createTestState(fileDescriptors) {
7+
return fileDescriptors.map((f) => ({
8+
id: f.id,
9+
_id: f.id,
10+
name: f.name,
11+
content: f.content ?? '',
12+
fileType: f.fileType ?? 'file',
13+
children: f.children ?? [],
14+
parentId: f.parentId ?? null
15+
}));
16+
}
17+
18+
describe('files reducer', () => {
19+
describe('DELETE_FILE action', () => {
20+
it('removes file and updates parent children array', () => {
21+
const state = createTestState([
22+
{
23+
id: 'root',
24+
name: 'root',
25+
fileType: 'folder',
26+
children: ['sketch', 'html', 'css']
27+
},
28+
{
29+
id: 'sketch',
30+
name: 'sketch.js',
31+
content: 'draw()',
32+
parentId: 'root'
33+
},
34+
{ id: 'html', name: 'index.html', content: '<html>', parentId: 'root' },
35+
{ id: 'css', name: 'style.css', content: 'body {}', parentId: 'root' }
36+
]);
37+
38+
const action = {
39+
type: ActionTypes.DELETE_FILE,
40+
id: 'sketch',
41+
parentId: 'root'
42+
};
43+
44+
const newState = files(state, action);
45+
46+
// File should be removed from state
47+
expect(newState.find((f) => f.id === 'sketch')).toBeUndefined();
48+
expect(newState).toHaveLength(state.length - 1);
49+
50+
// Parent's children array should be updated
51+
const root = newState.find((f) => f.id === 'root');
52+
expect(root.children).not.toContain('sketch');
53+
expect(root.children).toHaveLength(2);
54+
55+
// Siblings should still exist with unchanged content
56+
const htmlFile = newState.find((f) => f.id === 'html');
57+
const cssFile = newState.find((f) => f.id === 'css');
58+
expect(htmlFile).toBeDefined();
59+
expect(cssFile).toBeDefined();
60+
expect(htmlFile.content).toBe('<html>');
61+
expect(htmlFile.name).toBe('index.html');
62+
expect(cssFile.content).toBe('body {}');
63+
});
64+
65+
it('recursively deletes folder and all descendants', () => {
66+
const state = createTestState([
67+
{
68+
id: 'root',
69+
name: 'root',
70+
fileType: 'folder',
71+
children: ['components']
72+
},
73+
{
74+
id: 'components',
75+
name: 'components',
76+
fileType: 'folder',
77+
children: ['button', 'input'],
78+
parentId: 'root'
79+
},
80+
{ id: 'button', name: 'Button.jsx', parentId: 'components' },
81+
{ id: 'input', name: 'Input.jsx', parentId: 'components' }
82+
]);
83+
84+
const action = {
85+
type: ActionTypes.DELETE_FILE,
86+
id: 'components',
87+
parentId: 'root'
88+
};
89+
90+
const newState = files(state, action);
91+
92+
// All three items should be deleted
93+
expect(newState.find((f) => f.id === 'components')).toBeUndefined();
94+
expect(newState.find((f) => f.id === 'button')).toBeUndefined();
95+
expect(newState.find((f) => f.id === 'input')).toBeUndefined();
96+
expect(newState).toHaveLength(state.length - 3);
97+
98+
// Parent cleanup
99+
const root = newState.find((f) => f.id === 'root');
100+
expect(root.children).not.toContain('components');
101+
102+
// No orphaned files, every non root file should be referenced in some parent's children array
103+
const referencedIds = new Set();
104+
newState.forEach((file) => {
105+
if (file.children) {
106+
file.children.forEach((childId) => referencedIds.add(childId));
107+
}
108+
});
109+
110+
const nonRootFiles = newState.filter((f) => f.name !== 'root');
111+
nonRootFiles.forEach((file) => {
112+
expect(referencedIds.has(file.id)).toBe(true);
113+
});
114+
});
115+
116+
it('handles deeply nested folder hierarchies', () => {
117+
const state = createTestState([
118+
{
119+
id: 'root',
120+
name: 'root',
121+
fileType: 'folder',
122+
children: ['src']
123+
},
124+
{
125+
id: 'src',
126+
name: 'src',
127+
fileType: 'folder',
128+
children: ['utils'],
129+
parentId: 'root'
130+
},
131+
{
132+
id: 'utils',
133+
name: 'utils',
134+
fileType: 'folder',
135+
children: ['helper'],
136+
parentId: 'src'
137+
},
138+
{ id: 'helper', name: 'helper.js', parentId: 'utils' }
139+
]);
140+
141+
const action = {
142+
type: ActionTypes.DELETE_FILE,
143+
id: 'src',
144+
parentId: 'root'
145+
};
146+
147+
const newState = files(state, action);
148+
149+
// All three nested items should be deleted
150+
expect(newState.find((f) => f.id === 'src')).toBeUndefined();
151+
expect(newState.find((f) => f.id === 'utils')).toBeUndefined();
152+
expect(newState.find((f) => f.id === 'helper')).toBeUndefined();
153+
expect(newState).toHaveLength(state.length - 3);
154+
});
155+
156+
it('handles empty folder deletion', () => {
157+
const state = createTestState([
158+
{
159+
id: 'root',
160+
name: 'root',
161+
fileType: 'folder',
162+
children: ['docs']
163+
},
164+
{
165+
id: 'docs',
166+
name: 'docs',
167+
fileType: 'folder',
168+
children: [],
169+
parentId: 'root'
170+
}
171+
]);
172+
173+
const action = {
174+
type: ActionTypes.DELETE_FILE,
175+
id: 'docs',
176+
parentId: 'root'
177+
};
178+
179+
const newState = files(state, action);
180+
181+
// Folder should be removed
182+
expect(newState.find((f) => f.id === 'docs')).toBeUndefined();
183+
184+
// Parent should be updated
185+
const root = newState.find((f) => f.id === 'root');
186+
expect(root.children).not.toContain('docs');
187+
});
188+
});
189+
});

0 commit comments

Comments
 (0)