PrintModalDispatchToast.test.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * Test that create mode now goes through the queue-backed create path.
  3. *
  4. * Separate file because vi.mock(ToastContext) must be module-scoped
  5. * and would interfere with the main PrintModal test suite.
  6. */
  7. import { describe, it, expect, vi, beforeEach } from 'vitest';
  8. import { screen, waitFor } from '@testing-library/react';
  9. import userEvent from '@testing-library/user-event';
  10. import { http, HttpResponse } from 'msw';
  11. import { server } from '../mocks/server';
  12. // Mock the toast context before importing the component
  13. const mockShowToast = vi.fn();
  14. vi.mock('../../contexts/ToastContext', async (importOriginal) => {
  15. const actual = await importOriginal<typeof import('../../contexts/ToastContext')>();
  16. return {
  17. ...actual,
  18. useToast: () => ({ showToast: mockShowToast }),
  19. };
  20. });
  21. import { render } from '../utils';
  22. import { PrintModal } from '../../components/PrintModal';
  23. const mockPrinters = [
  24. { id: 1, name: 'X1 Carbon', model: 'X1C', ip_address: '192.168.1.100', enabled: true, is_active: true },
  25. ];
  26. describe('PrintModal dispatch toast', () => {
  27. const mockOnClose = vi.fn();
  28. const mockOnSuccess = vi.fn();
  29. beforeEach(() => {
  30. vi.clearAllMocks();
  31. server.use(
  32. http.get('/api/v1/printers/', () => {
  33. return HttpResponse.json(mockPrinters);
  34. }),
  35. http.get('/api/v1/archives/:id/plates', () => {
  36. return HttpResponse.json({ is_multi_plate: false, plates: [] });
  37. }),
  38. http.get('/api/v1/archives/:id/filament-requirements', () => {
  39. return HttpResponse.json({ filaments: [] });
  40. }),
  41. http.get('/api/v1/printers/:id/status', () => {
  42. return HttpResponse.json({ connected: true, state: 'IDLE', ams: [], vt_tray: [] });
  43. }),
  44. http.post('/api/v1/queue/', () => {
  45. return HttpResponse.json({ id: 1, status: 'pending' });
  46. }),
  47. );
  48. });
  49. it('shows queued toast in create mode', async () => {
  50. const user = userEvent.setup();
  51. render(
  52. <PrintModal
  53. mode="create"
  54. archiveId={1}
  55. archiveName="Benchy"
  56. onClose={mockOnClose}
  57. onSuccess={mockOnSuccess}
  58. />
  59. );
  60. // Wait for printers to load, then select one
  61. await waitFor(() => {
  62. expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
  63. });
  64. await user.click(screen.getByText('X1 Carbon'));
  65. // Submit the print
  66. const printButton = screen.getByRole('button', { name: /^print$/i });
  67. await user.click(printButton);
  68. // Wait for the API call to complete and modal to close
  69. await waitFor(() => {
  70. expect(mockOnClose).toHaveBeenCalled();
  71. });
  72. const toastMessages = mockShowToast.mock.calls.map(call => call[0]);
  73. expect(toastMessages).toContain('Print queued');
  74. });
  75. it('uses wait-for-idle copy when an ASAP target is offline', async () => {
  76. server.use(
  77. http.get('/api/v1/printers/:id/status', () => {
  78. return HttpResponse.json({ connected: false, state: null, ams: [], vt_tray: [] });
  79. }),
  80. );
  81. const user = userEvent.setup();
  82. render(
  83. <PrintModal
  84. mode="create"
  85. archiveId={1}
  86. archiveName="Benchy"
  87. initialSelectedPrinterIds={[1]}
  88. onClose={mockOnClose}
  89. onSuccess={mockOnSuccess}
  90. />
  91. );
  92. await waitFor(() => {
  93. expect(screen.getByRole('button', { name: /^print$/i })).toBeInTheDocument();
  94. });
  95. await user.click(screen.getByRole('button', { name: /^print$/i }));
  96. await waitFor(() => {
  97. expect(mockOnClose).toHaveBeenCalled();
  98. });
  99. const toastMessages = mockShowToast.mock.calls.map(call => call[0]);
  100. expect(toastMessages).toContain('Will start when printer is idle');
  101. expect(toastMessages).not.toContain('Print queued');
  102. });
  103. it('uses wait-for-idle copy when an ASAP target is held for plate clear', async () => {
  104. server.use(
  105. http.get('/api/v1/printers/:id/status', () => {
  106. return HttpResponse.json({
  107. connected: true,
  108. state: 'FINISH',
  109. awaiting_plate_clear: true,
  110. ams: [],
  111. vt_tray: [],
  112. });
  113. }),
  114. );
  115. const user = userEvent.setup();
  116. render(
  117. <PrintModal
  118. mode="create"
  119. archiveId={1}
  120. archiveName="Benchy"
  121. initialSelectedPrinterIds={[1]}
  122. onClose={mockOnClose}
  123. onSuccess={mockOnSuccess}
  124. />
  125. );
  126. await waitFor(() => {
  127. expect(screen.getByRole('button', { name: /^print$/i })).toBeInTheDocument();
  128. });
  129. await user.click(screen.getByRole('button', { name: /^print$/i }));
  130. await waitFor(() => {
  131. expect(mockOnClose).toHaveBeenCalled();
  132. });
  133. const toastMessages = mockShowToast.mock.calls.map(call => call[0]);
  134. expect(toastMessages).toContain('Will start when printer is idle');
  135. });
  136. it('uses wait-for-idle copy when an ASAP target is drying filament', async () => {
  137. server.use(
  138. http.get('/api/v1/printers/:id/status', () => {
  139. return HttpResponse.json({
  140. connected: true,
  141. state: 'IDLE',
  142. awaiting_plate_clear: false,
  143. ams: [{ id: 0, dry_time: 25, tray: [] }],
  144. vt_tray: [],
  145. });
  146. }),
  147. );
  148. const user = userEvent.setup();
  149. render(
  150. <PrintModal
  151. mode="create"
  152. archiveId={1}
  153. archiveName="Benchy"
  154. initialSelectedPrinterIds={[1]}
  155. onClose={mockOnClose}
  156. onSuccess={mockOnSuccess}
  157. />
  158. );
  159. await waitFor(() => {
  160. expect(screen.getByRole('button', { name: /^print$/i })).toBeInTheDocument();
  161. });
  162. await user.click(screen.getByRole('button', { name: /^print$/i }));
  163. await waitFor(() => {
  164. expect(mockOnClose).toHaveBeenCalled();
  165. });
  166. const toastMessages = mockShowToast.mock.calls.map(call => call[0]);
  167. expect(toastMessages).toContain('Will start when printer is idle');
  168. });
  169. });