-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathFileTreePanel.test.tsx
More file actions
140 lines (115 loc) · 5.37 KB
/
Copy pathFileTreePanel.test.tsx
File metadata and controls
140 lines (115 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FileTreePanel } from '@/components/review/FileTreePanel';
import type { FileChange, Task } from '@/types';
// jsdom doesn't provide ResizeObserver (needed by radix ScrollArea)
global.ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
} as unknown as typeof ResizeObserver;
// ─── Fixtures ───────────────────────────────────────────────────────
const mockFiles: FileChange[] = [
{ path: 'src/foo.ts', change_type: 'modified', insertions: 5, deletions: 2, task_id: 'task-1', task_title: 'Add login' },
{ path: 'src/bar.ts', change_type: 'added', insertions: 10, deletions: 0 },
{ path: 'lib/utils.ts', change_type: 'modified', insertions: 3, deletions: 1, task_id: 'task-2', task_title: 'Fix utils' },
];
const mockTasks: Task[] = [
{ id: 'task-1', title: 'Add login', description: '', status: 'IN_PROGRESS', priority: 1, depends_on: [] },
{ id: 'task-2', title: 'Fix utils', description: '', status: 'READY', priority: 2, depends_on: [] },
];
const defaultProps = {
files: mockFiles,
selectedFile: null,
onFileSelect: jest.fn(),
};
beforeEach(() => {
jest.clearAllMocks();
});
// ─── Tests ──────────────────────────────────────────────────────────
describe('FileTreePanel', () => {
it('renders file list grouped by directory', () => {
render(<FileTreePanel {...defaultProps} />);
expect(screen.getByText('src')).toBeInTheDocument();
expect(screen.getByText('lib')).toBeInTheDocument();
expect(screen.getByText('foo.ts')).toBeInTheDocument();
expect(screen.getByText('bar.ts')).toBeInTheDocument();
expect(screen.getByText('utils.ts')).toBeInTheDocument();
});
it('renders a grouping toggle button when tasks prop has items', () => {
render(<FileTreePanel {...defaultProps} tasks={mockTasks} />);
expect(screen.getByRole('button', { name: /task/i })).toBeInTheDocument();
});
it('does not render a grouping toggle when tasks is empty', () => {
render(<FileTreePanel {...defaultProps} tasks={[]} />);
expect(screen.queryByRole('button', { name: /task/i })).not.toBeInTheDocument();
});
it('does not render a grouping toggle when tasks is undefined', () => {
render(<FileTreePanel {...defaultProps} />);
expect(screen.queryByRole('button', { name: /task/i })).not.toBeInTheDocument();
});
it('groups files under task headers when toggled to task mode', async () => {
const user = userEvent.setup();
render(<FileTreePanel {...defaultProps} tasks={mockTasks} />);
await user.click(screen.getByRole('button', { name: /task/i }));
// Task group headers should appear
expect(screen.getByText('Add login')).toBeInTheDocument();
expect(screen.getByText('Fix utils')).toBeInTheDocument();
expect(screen.getByText('Unassigned')).toBeInTheDocument();
});
it('shows task title badge next to filename in dir mode when file has task_title', () => {
render(<FileTreePanel {...defaultProps} tasks={mockTasks} />);
// In dir mode (default), files with task_title should show a badge
const badges = screen.getAllByText('Add login');
expect(badges.length).toBeGreaterThanOrEqual(1);
});
it('groups untagged files under contextTask when contextTask is provided', async () => {
const user = userEvent.setup();
const contextTask = mockTasks[0]; // 'Add login'
const untaggedFiles: FileChange[] = [
{ path: 'src/untagged.ts', change_type: 'modified', insertions: 1, deletions: 0 },
];
render(
<FileTreePanel
files={untaggedFiles}
selectedFile={null}
onFileSelect={jest.fn()}
tasks={mockTasks}
contextTask={contextTask}
/>
);
await user.click(screen.getByRole('button', { name: /group by task/i }));
// Untagged file should appear under the contextTask group, not 'Unassigned'
expect(screen.getByText('Add login')).toBeInTheDocument();
expect(screen.queryByText('Unassigned')).not.toBeInTheDocument();
});
it('shows contextTask title as badge in dir mode for files without task_title', () => {
const contextTask = mockTasks[0]; // 'Add login'
const untaggedFiles: FileChange[] = [
{ path: 'src/untagged.ts', change_type: 'modified', insertions: 1, deletions: 0 },
];
render(
<FileTreePanel
files={untaggedFiles}
selectedFile={null}
onFileSelect={jest.fn()}
tasks={mockTasks}
contextTask={contextTask}
/>
);
expect(screen.getByText('Add login')).toBeInTheDocument();
});
it('task groups are collapsible', async () => {
const user = userEvent.setup();
render(<FileTreePanel {...defaultProps} tasks={mockTasks} />);
// Switch to task mode
await user.click(screen.getByRole('button', { name: /task/i }));
// Files should be visible
expect(screen.getByText('foo.ts')).toBeInTheDocument();
// Click on task group header to collapse
const addLoginHeader = screen.getByRole('button', { name: /collapse add login/i });
await user.click(addLoginHeader);
// foo.ts should no longer be visible
expect(screen.queryByText('foo.ts')).not.toBeInTheDocument();
});
});