/** * Tests for the FileManagerPage component. */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { render } from '../utils'; import { FileManagerPage } from '../../pages/FileManagerPage'; import { http, HttpResponse } from 'msw'; import { server } from '../mocks/server'; // Mock data const mockFolders = [ { id: 1, name: 'Functional Parts', parent_id: null, file_count: 5, project_id: null, archive_id: null, project_name: null, archive_name: null, children: [ { id: 2, name: 'Brackets', parent_id: 1, file_count: 3, project_id: null, archive_id: null, project_name: null, archive_name: null, children: [], }, ], }, { id: 3, name: 'Art Projects', parent_id: null, file_count: 2, project_id: 1, archive_id: null, project_name: 'My Art Project', archive_name: null, children: [], }, ]; const mockFiles = [ { id: 1, filename: 'benchy.gcode.3mf', file_path: '/library/benchy.gcode.3mf', file_size: 1048576, file_type: '3mf', folder_id: null, thumbnail_path: '/thumbnails/1.png', print_name: 'Benchy', print_time_seconds: 3600, print_count: 5, duplicate_count: 0, created_at: '2024-01-01T00:00:00Z', }, { id: 2, filename: 'bracket.stl', file_path: '/library/bracket.stl', file_size: 524288, file_type: 'stl', folder_id: null, thumbnail_path: null, print_name: null, print_time_seconds: null, print_count: 0, duplicate_count: 2, created_at: '2024-01-02T00:00:00Z', }, { id: 3, filename: 'cube.gcode.3mf', file_path: '/library/cube.gcode.3mf', file_size: 2048576, file_type: '3mf', folder_id: null, thumbnail_path: '/thumbnails/3.png', print_name: 'Cube', print_time_seconds: 1800, print_count: 2, duplicate_count: 0, created_at: '2024-01-03T00:00:00Z', }, ]; const mockStats = { total_files: 10, total_folders: 3, total_size_bytes: 104857600, disk_free_bytes: 10737418240, disk_total_bytes: 107374182400, }; describe('FileManagerPage', () => { beforeEach(() => { // Clear localStorage to ensure consistent view mode localStorage.clear(); server.use( http.get('/api/v1/library/folders', () => { return HttpResponse.json(mockFolders); }), http.get('/api/v1/library/files', () => { return HttpResponse.json(mockFiles); }), http.get('/api/v1/library/stats', () => { return HttpResponse.json(mockStats); }), http.get('/api/v1/settings/', () => { return HttpResponse.json({ check_updates: false, check_printer_firmware: false, library_disk_warning_gb: 5, }); }), http.post('/api/v1/library/folders', async ({ request }) => { const body = await request.json() as { name: string }; return HttpResponse.json({ id: 4, name: body.name, parent_id: null, children: [] }); }), http.delete('/api/v1/library/folders/:id', () => { return HttpResponse.json({ success: true }); }), http.delete('/api/v1/library/files/:id', () => { return HttpResponse.json({ success: true }); }), http.post('/api/v1/library/files/move', () => { return HttpResponse.json({ success: true }); }), http.post('/api/v1/library/files/add-to-queue', () => { return HttpResponse.json({ added: [{ file_id: 1, queue_id: 1 }], errors: [] }); }), http.get('/api/v1/projects/', () => { return HttpResponse.json([{ id: 1, name: 'Test Project', color: '#00ae42' }]); }), http.get('/api/v1/archives/', () => { return HttpResponse.json([{ id: 1, print_name: 'Test Archive', filename: 'test.3mf' }]); }) ); }); describe('rendering', () => { it('renders the page title', async () => { render(); await waitFor(() => { expect(screen.getByText('File Manager')).toBeInTheDocument(); }); }); it('renders the page description', async () => { render(); await waitFor(() => { expect(screen.getByText('Organize and manage your print files')).toBeInTheDocument(); }); }); it('shows New Folder button', async () => { render(); await waitFor(() => { expect(screen.getByText('New Folder')).toBeInTheDocument(); }); }); it('shows Upload button', async () => { render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); }); }); describe('stats display', () => { it('shows file count', async () => { render(); await waitFor(() => { expect(screen.getByText('Files:')).toBeInTheDocument(); expect(screen.getByText('10')).toBeInTheDocument(); }); }); it('shows folder count', async () => { render(); await waitFor(() => { expect(screen.getByText('Folders:')).toBeInTheDocument(); // Folder count appears multiple places, just verify the label is present const foldersLabel = screen.getByText('Folders:'); expect(foldersLabel.nextElementSibling?.textContent).toBe('3'); }); }); it('shows total size', async () => { render(); await waitFor(() => { expect(screen.getByText('Size:')).toBeInTheDocument(); expect(screen.getByText('100.0 MB')).toBeInTheDocument(); }); }); it('shows free space', async () => { render(); await waitFor(() => { expect(screen.getByText('Free:')).toBeInTheDocument(); }); }); }); describe('folder sidebar', () => { it('shows All Files option', async () => { render(); await waitFor(() => { expect(screen.getByText('All Files')).toBeInTheDocument(); }); }); it('shows folder tree', async () => { render(); await waitFor(() => { expect(screen.getByText('Functional Parts')).toBeInTheDocument(); expect(screen.getByText('Art Projects')).toBeInTheDocument(); }); }); it('shows nested folders', async () => { render(); await waitFor(() => { expect(screen.getByText('Brackets')).toBeInTheDocument(); }); }); it('shows linked folder indicator', async () => { render(); await waitFor(() => { // Art Projects has a project_id expect(screen.getByText('Art Projects')).toBeInTheDocument(); }); }); }); describe('file display', () => { it('shows files in grid', async () => { render(); await waitFor(() => { expect(screen.getByText('Benchy')).toBeInTheDocument(); }); }); it('shows file type badges', async () => { render(); await waitFor(() => { // File type badges show uppercase type expect(screen.getAllByText('3MF').length).toBeGreaterThan(0); expect(screen.getAllByText('STL').length).toBeGreaterThan(0); }); }); it('shows print count', async () => { render(); await waitFor(() => { expect(screen.getByText('Printed 5x')).toBeInTheDocument(); }); }); it('shows duplicate badge', async () => { render(); await waitFor(() => { // Duplicate badge shows count, there may be multiple "2"s on the page // so we check that at least one element with "2" exists const elements = screen.getAllByText('2'); expect(elements.length).toBeGreaterThan(0); }); }); }); describe('view modes', () => { it('has grid view button', async () => { render(); await waitFor(() => { expect(screen.getByTitle('Grid view')).toBeInTheDocument(); }); }); it('has list view button', async () => { render(); await waitFor(() => { expect(screen.getByTitle('List view')).toBeInTheDocument(); }); }); it('can switch to list view', async () => { const user = userEvent.setup(); render(); // Wait for files to load first await waitFor(() => { expect(screen.getByText('Benchy')).toBeInTheDocument(); }); // Both view mode buttons should be present and clickable const gridButton = screen.getByTitle('Grid view'); const listButton = screen.getByTitle('List view'); expect(gridButton).toBeInTheDocument(); expect(listButton).toBeInTheDocument(); // Click list view button - verify no errors occur await user.click(listButton); // Clicking grid button should also work await user.click(gridButton); // Verify files are still displayed after toggling expect(screen.getByText('Benchy')).toBeInTheDocument(); }); }); describe('search and filter', () => { it('has search input', async () => { render(); await waitFor(() => { expect(screen.getByPlaceholderText('Search files...')).toBeInTheDocument(); }); }); it('has type filter', async () => { render(); await waitFor(() => { expect(screen.getByText('All types')).toBeInTheDocument(); }); }); it('has sort options', async () => { render(); await waitFor(() => { // Sort dropdown should show Name as default option (persisted to localStorage) expect(screen.getByDisplayValue('Name')).toBeInTheDocument(); }); }); }); describe('selection', () => { it('shows select all button', async () => { render(); await waitFor(() => { expect(screen.getByText('Select All')).toBeInTheDocument(); }); }); it('can select files', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Benchy')).toBeInTheDocument(); }); // Click on the file card to select it const fileCard = screen.getByText('Benchy').closest('div[class*="cursor-pointer"]'); if (fileCard) { await user.click(fileCard); } await waitFor(() => { expect(screen.getByText('1 selected')).toBeInTheDocument(); }); }); it('shows bulk actions when files selected', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Select All')).toBeInTheDocument(); }); await user.click(screen.getByText('Select All')); await waitFor(() => { expect(screen.getByText('Move')).toBeInTheDocument(); expect(screen.getByText('Delete')).toBeInTheDocument(); }); }); }); describe('new folder modal', () => { it('opens new folder modal', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('New Folder')).toBeInTheDocument(); }); await user.click(screen.getByText('New Folder')); await waitFor(() => { expect(screen.getByText('Folder Name')).toBeInTheDocument(); expect(screen.getByPlaceholderText('e.g., Functional Parts')).toBeInTheDocument(); }); }); it('can create a folder', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('New Folder')).toBeInTheDocument(); }); await user.click(screen.getByText('New Folder')); await waitFor(() => { expect(screen.getByPlaceholderText('e.g., Functional Parts')).toBeInTheDocument(); }); const input = screen.getByPlaceholderText('e.g., Functional Parts'); await user.type(input, 'My New Folder'); const createButton = screen.getByRole('button', { name: 'Create' }); await user.click(createButton); // Modal should close after creation await waitFor(() => { expect(screen.queryByText('Folder Name')).not.toBeInTheDocument(); }); }); }); describe('empty state', () => { it('shows empty state when no files', async () => { server.use( http.get('/api/v1/library/files', () => { return HttpResponse.json([]); }) ); render(); await waitFor(() => { expect(screen.getByText('No files yet')).toBeInTheDocument(); expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); }); }); describe('schedule print', () => { it('shows schedule print button when one sliced file is selected', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Benchy')).toBeInTheDocument(); }); // Select a sliced file (benchy.gcode.3mf) by clicking on its card const fileCard = screen.getByText('Benchy').closest('div[class*="cursor-pointer"]'); if (fileCard) { await user.click(fileCard); } await waitFor(() => { expect(screen.getByText(/Schedule/)).toBeInTheDocument(); }); }); it('hides schedule print button when multiple files are selected', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Select All')).toBeInTheDocument(); }); // Select all files await user.click(screen.getByText('Select All')); await waitFor(() => { // Schedule button should not be present when multiple files are selected expect(screen.queryByText(/Schedule/)).not.toBeInTheDocument(); }); }); }); describe('STL thumbnail generation', () => { it('shows Generate Thumbnails button', async () => { render(); await waitFor(() => { expect(screen.getByText('Generate Thumbnails')).toBeInTheDocument(); }); }); it('Generate Thumbnails button has correct title', async () => { render(); await waitFor(() => { const button = screen.getByTitle('Generate thumbnails for STL files missing them'); expect(button).toBeInTheDocument(); }); }); it('can click Generate Thumbnails button', async () => { const user = userEvent.setup(); server.use( http.post('/api/v1/library/generate-stl-thumbnails', () => { return HttpResponse.json({ processed: 1, succeeded: 1, failed: 0, results: [{ file_id: 2, success: true }], }); }) ); render(); await waitFor(() => { expect(screen.getByText('Generate Thumbnails')).toBeInTheDocument(); }); const button = screen.getByText('Generate Thumbnails'); await user.click(button); // Button should work without error await waitFor(() => { expect(screen.getByText('Generate Thumbnails')).toBeInTheDocument(); }); }); it('shows STL file without thumbnail in file list', async () => { render(); await waitFor(() => { // bracket.stl has no thumbnail_path expect(screen.getByText('bracket.stl')).toBeInTheDocument(); expect(screen.getAllByText('STL').length).toBeGreaterThan(0); }); }); }); describe('upload modal (FileUploadModal)', () => { it('opens upload modal when Upload button is clicked', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); expect(screen.getByText(/Drag & drop/)).toBeInTheDocument(); }); }); it('closes upload modal when Cancel is clicked', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); await user.click(screen.getByRole('button', { name: 'Cancel' })); await waitFor(() => { expect(screen.queryByText('Upload Files')).not.toBeInTheDocument(); }); }); it('shows 3MF extraction info when 3MF file is added', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); const threemfFile = new File(['content'], 'model.gcode.3mf', { type: 'application/octet-stream' }); const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; expect(fileInput).toBeInTheDocument(); await user.upload(fileInput, threemfFile); await waitFor(() => { expect(screen.getByText('3MF files detected')).toBeInTheDocument(); expect(screen.getByText(/Printer model.*will be automatically extracted/i)).toBeInTheDocument(); }); }); it('shows STL thumbnail option when STL file is added', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); const stlFile = new File(['solid test'], 'model.stl', { type: 'application/sla' }); const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; expect(fileInput).toBeInTheDocument(); await user.upload(fileInput, stlFile); await waitFor(() => { expect(screen.getByText('STL thumbnail generation')).toBeInTheDocument(); expect(screen.getByText(/Thumbnails can be generated/i)).toBeInTheDocument(); }); }); it('shows ZIP options when ZIP file is added', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); const zipFile = new File(['pk'], 'models.zip', { type: 'application/zip' }); const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; await user.upload(fileInput, zipFile); await waitFor(() => { expect(screen.getByText('ZIP files detected')).toBeInTheDocument(); expect(screen.getByText(/Preserve folder structure/)).toBeInTheDocument(); }); }); it('can add a file via the file input', async () => { const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); const file = new File(['content'], 'model.3mf', { type: 'application/octet-stream' }); const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; await user.upload(fileInput, file); await waitFor(() => { expect(screen.getByText('model.3mf')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /Upload \(1\)/i })).toBeInTheDocument(); }); }); it('uploads file and refreshes file list', async () => { server.use( http.post('/api/v1/library/files', () => { return HttpResponse.json({ id: 10, filename: 'uploaded.3mf', file_type: '3mf', file_size: 1024, thumbnail_path: null, duplicate_of: null, metadata: null, }); }) ); const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Upload')).toBeInTheDocument(); }); await user.click(screen.getByText('Upload')); await waitFor(() => { expect(screen.getByText('Upload Files')).toBeInTheDocument(); }); const file = new File(['content'], 'uploaded.3mf', { type: 'application/octet-stream' }); const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement; await user.upload(fileInput, file); const uploadButton = screen.getByRole('button', { name: /Upload \(1\)/i }); await user.click(uploadButton); // Modal should auto-close after upload completes await waitFor(() => { expect(screen.queryByText('Upload Files')).not.toBeInTheDocument(); }); }); }); describe('authentication-based UI changes', () => { it('hides "Uploaded By" column and user filter when auth is disabled', async () => { // Mock auth disabled (default) server.use( http.get('*/api/v1/auth/status', () => { return HttpResponse.json({ auth_enabled: false, requires_setup: false, }); }), http.get('/api/v1/library/files', () => { return HttpResponse.json([ { id: 1, filename: 'test.3mf', file_path: '/library/test.3mf', file_size: 1048576, file_type: '3mf', folder_id: null, thumbnail_path: null, print_name: 'Test File', print_time_seconds: 3600, print_count: 0, duplicate_count: 0, created_at: '2024-01-01T00:00:00Z', created_by_username: 'testuser', }, ]); }) ); render(); // Switch to list view to see the column headers await waitFor(() => { expect(screen.getByText('Test File')).toBeInTheDocument(); }); const user = userEvent.setup(); const listViewButton = screen.getByRole('button', { name: /list/i }); await user.click(listViewButton); // "Uploaded By" column header should not be present await waitFor(() => { expect(screen.queryByText('Uploaded By')).not.toBeInTheDocument(); }); // User filter dropdown should not be present expect(screen.queryByPlaceholderText('Filter by user')).not.toBeInTheDocument(); }); it('shows "Uploaded By" column and user filter when auth is enabled', async () => { // Mock auth enabled server.use( http.get('*/api/v1/auth/status', () => { return HttpResponse.json({ auth_enabled: true, requires_setup: false, }); }), http.get('/api/v1/library/files', () => { return HttpResponse.json([ { id: 1, filename: 'test.3mf', file_path: '/library/test.3mf', file_size: 1048576, file_type: '3mf', folder_id: null, thumbnail_path: null, print_name: 'Test File', print_time_seconds: 3600, print_count: 0, duplicate_count: 0, created_at: '2024-01-01T00:00:00Z', created_by_username: 'testuser', }, ]); }), http.get('/api/v1/users/', () => { return HttpResponse.json([ { id: 1, username: 'testuser' }, { id: 2, username: 'admin' }, ]); }) ); render(); // Switch to list view to see the column headers await waitFor(() => { expect(screen.getByText('Test File')).toBeInTheDocument(); }); const user = userEvent.setup(); const listViewButton = screen.getByRole('button', { name: /list/i }); await user.click(listViewButton); // "Uploaded By" column header should be present await waitFor(() => { expect(screen.getByText('Uploaded By')).toBeInTheDocument(); }); // User filter dropdown should be present expect(screen.getByPlaceholderText('Filter by user')).toBeInTheDocument(); // Username should be displayed in the column expect(screen.getByText('testuser')).toBeInTheDocument(); }); }); describe('folder tree collapse preference (#996)', () => { // localStorage is globally mocked in setup.ts (returns undefined by default), // so we program each test's getItem return value explicitly. const getItemMock = localStorage.getItem as ReturnType; const setItemMock = localStorage.setItem as ReturnType; beforeEach(() => { getItemMock.mockReset(); setItemMock.mockReset(); }); it('defaults to expanded (nested folders visible) when library-collapse-folders is unset', async () => { getItemMock.mockReturnValue(null); render(); await waitFor(() => { expect(screen.getByText('Functional Parts')).toBeInTheDocument(); }); expect(screen.getByText('Brackets')).toBeInTheDocument(); }); it('honors library-collapse-folders=true on load (nested folders hidden)', async () => { getItemMock.mockImplementation((key: string) => key === 'library-collapse-folders' ? 'true' : null ); render(); await waitFor(() => { expect(screen.getByText('Functional Parts')).toBeInTheDocument(); }); expect(screen.queryByText('Brackets')).not.toBeInTheDocument(); }); it('collapses nested folders and persists preference when Collapse is clicked', async () => { getItemMock.mockReturnValue(null); const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Brackets')).toBeInTheDocument(); }); // The Collapse button sits next to Wrap in the sidebar header. // Its text content is "Collapse" (from fileManager.collapse). await user.click(screen.getByRole('button', { name: 'Collapse' })); await waitFor(() => { expect(screen.queryByText('Brackets')).not.toBeInTheDocument(); }); expect(setItemMock).toHaveBeenCalledWith('library-collapse-folders', 'true'); }); it('re-expands nested folders and persists preference when Collapse is toggled off', async () => { getItemMock.mockImplementation((key: string) => key === 'library-collapse-folders' ? 'true' : null ); const user = userEvent.setup(); render(); await waitFor(() => { expect(screen.getByText('Functional Parts')).toBeInTheDocument(); }); expect(screen.queryByText('Brackets')).not.toBeInTheDocument(); await user.click(screen.getByRole('button', { name: 'Collapse' })); await waitFor(() => { expect(screen.getByText('Brackets')).toBeInTheDocument(); }); expect(setItemMock).toHaveBeenCalledWith('library-collapse-folders', 'false'); }); }); describe('Internal / External top-level views (#1621)', () => { const externalMockFolders = [ ...mockFolders, { id: 99, name: 'NAS Library', parent_id: null, file_count: 200, project_id: null, archive_id: null, project_name: null, archive_name: null, is_external: true, external_readonly: false, external_path: '/mnt/nas', children: [], }, ]; it('shows the External sidebar entry only when at least one external folder is linked', async () => { // Default mockFolders have no is_external entries → no External row. const { unmount } = render(); await waitFor(() => { expect(screen.getByText('All Files')).toBeInTheDocument(); }); expect(screen.queryByText('External')).not.toBeInTheDocument(); unmount(); // With an external folder linked, the row appears. server.use( http.get('/api/v1/library/folders', () => HttpResponse.json(externalMockFolders)), ); render(); await waitFor(() => { expect(screen.getByText('External')).toBeInTheDocument(); }); }); it('sends internal_only=true by default ("All Files" = managed storage only)', async () => { const scopes: string[] = []; server.use( http.get('/api/v1/library/folders', () => HttpResponse.json(externalMockFolders)), http.get('/api/v1/library/files', ({ request }) => { const url = new URL(request.url); scopes.push( url.searchParams.get('internal_only') === 'true' ? 'internal' : url.searchParams.get('external_only') === 'true' ? 'external' : 'all', ); return HttpResponse.json(mockFiles); }), ); render(); await waitFor(() => { expect(scopes).toContain('internal'); }); }); it('switches to external_only=true when the External sidebar entry is clicked', async () => { const scopes: string[] = []; server.use( http.get('/api/v1/library/folders', () => HttpResponse.json(externalMockFolders)), http.get('/api/v1/library/files', ({ request }) => { const url = new URL(request.url); scopes.push( url.searchParams.get('internal_only') === 'true' ? 'internal' : url.searchParams.get('external_only') === 'true' ? 'external' : 'all', ); return HttpResponse.json([]); }), ); const { default: userEvent } = await import('@testing-library/user-event'); const user = userEvent.setup(); render(); await waitFor(() => expect(screen.getByText('External')).toBeInTheDocument()); await user.click(screen.getByText('External')); await waitFor(() => { expect(scopes).toContain('external'); }); }); }); describe('"All Files" view (#1499)', () => { it('requests every file (include_root=false) so subfolder contents are visible', async () => { const rootFile = { id: 10, filename: 'root-file.3mf', file_path: '/library/root-file.3mf', file_size: 1024, file_type: '3mf', folder_id: null, thumbnail_path: null, print_name: 'Root File', print_time_seconds: 0, print_count: 0, duplicate_count: 0, created_at: '2024-01-01T00:00:00Z', }; const nestedFile = { ...rootFile, id: 11, filename: 'nested-file.3mf', file_path: '/library/Functional Parts/nested-file.3mf', folder_id: 1, print_name: 'Nested File', }; const includeRootValues: string[] = []; server.use( http.get('/api/v1/library/files', ({ request }) => { const url = new URL(request.url); const includeRoot = url.searchParams.get('include_root'); includeRootValues.push(includeRoot ?? ''); // Mirror the backend: include_root=false returns everything; true // returns only files with folder_id IS NULL. if (includeRoot === 'false') { return HttpResponse.json([rootFile, nestedFile]); } return HttpResponse.json([rootFile]); }), ); render(); await waitFor(() => { expect(screen.getByText('Root File')).toBeInTheDocument(); expect(screen.getByText('Nested File')).toBeInTheDocument(); }); // Sanity-check: the buggy call would have sent include_root=true here. expect(includeRootValues).toContain('false'); }); }); });