CombineFilesModal.test.tsx 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /**
  2. * Tests for CombineFilesModal: STLs -> one multi-object 3MF (#2999).
  3. */
  4. import { describe, it, expect, vi, beforeEach } from 'vitest';
  5. import { render, screen, waitFor } from '@testing-library/react';
  6. import userEvent from '@testing-library/user-event';
  7. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  8. import { CombineFilesModal } from '../../components/CombineFilesModal';
  9. import { api, type LibraryFileListItem } from '../../api/client';
  10. const mockShowToast = vi.fn();
  11. const mockOnClose = vi.fn();
  12. const mockOnCombined = vi.fn();
  13. vi.mock('../../api/client', () => ({
  14. api: {
  15. combineLibraryFiles: vi.fn(),
  16. },
  17. }));
  18. vi.mock('../../contexts/ToastContext', () => ({
  19. useToast: () => ({ showToast: mockShowToast }),
  20. }));
  21. function file(id: number, filename: string): LibraryFileListItem {
  22. return { id, filename, file_type: 'stl' } as LibraryFileListItem;
  23. }
  24. function renderModal(files: LibraryFileListItem[], canSlice = true) {
  25. const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  26. return render(
  27. <QueryClientProvider client={client}>
  28. <CombineFilesModal
  29. files={files}
  30. folderId={7}
  31. canSlice={canSlice}
  32. onClose={mockOnClose}
  33. onCombined={mockOnCombined}
  34. />
  35. </QueryClientProvider>,
  36. );
  37. }
  38. describe('CombineFilesModal', () => {
  39. beforeEach(() => {
  40. vi.clearAllMocks();
  41. });
  42. it('suggests a name from the first file and lists every model', () => {
  43. renderModal([file(1, 'bracket.stl'), file(2, 'clip.stl'), file(3, 'knob.stl')]);
  44. expect(screen.getByLabelText('File name')).toHaveValue('bracket + 2 more');
  45. expect(screen.getByText('clip.stl')).toBeInTheDocument();
  46. expect(screen.getByText('Objects on the plate: 3')).toBeInTheDocument();
  47. });
  48. it('sends copies, name and folder, then hands the new file on for slicing', async () => {
  49. const result = { id: 99, filename: 'bracket x4.3mf', file_type: '3mf', file_size: 1, thumbnail_path: null, duplicate_of: null, metadata: null };
  50. (api.combineLibraryFiles as ReturnType<typeof vi.fn>).mockResolvedValue(result);
  51. const user = userEvent.setup();
  52. renderModal([file(1, 'bracket.stl')]);
  53. const copies = screen.getByLabelText('Copies of bracket.stl');
  54. await user.clear(copies);
  55. await user.type(copies, '4');
  56. expect(screen.getByLabelText('File name')).toHaveValue('bracket x4');
  57. await user.click(screen.getByRole('button', { name: 'Combine' }));
  58. await waitFor(() => {
  59. expect(api.combineLibraryFiles).toHaveBeenCalledWith([{ file_id: 1, copies: 4 }], 'bracket x4', 7);
  60. });
  61. expect(mockOnCombined).toHaveBeenCalledWith(result, true);
  62. });
  63. it('blocks more objects than one plate allows', async () => {
  64. const user = userEvent.setup();
  65. renderModal([file(1, 'a.stl'), file(2, 'b.stl')]);
  66. const copies = screen.getByLabelText('Copies of a.stl');
  67. await user.clear(copies);
  68. await user.type(copies, '100');
  69. expect(screen.getByText('At most 100 objects per plate')).toBeInTheDocument();
  70. expect(screen.getByRole('button', { name: 'Combine' })).toBeDisabled();
  71. });
  72. it('hides the slice option when in-app slicing is unavailable', async () => {
  73. (api.combineLibraryFiles as ReturnType<typeof vi.fn>).mockResolvedValue({ id: 5, filename: 'x.3mf' });
  74. const user = userEvent.setup();
  75. renderModal([file(1, 'a.stl')], false);
  76. expect(screen.queryByText('Open the slicer when done')).not.toBeInTheDocument();
  77. await user.click(screen.getByRole('button', { name: 'Combine' }));
  78. await waitFor(() => expect(mockOnCombined).toHaveBeenCalledWith({ id: 5, filename: 'x.3mf' }, false));
  79. });
  80. });