AddPrinterDiscovery.test.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * Tests for AddPrinterModal discovery subnet auto-detection.
  3. */
  4. import { describe, it, expect, beforeEach } from 'vitest';
  5. import { screen, waitFor } from '@testing-library/react';
  6. import userEvent from '@testing-library/user-event';
  7. import { render } from '../utils';
  8. import { PrintersPage } from '../../pages/PrintersPage';
  9. import { http, HttpResponse } from 'msw';
  10. import { server } from '../mocks/server';
  11. const mockPrinters = [
  12. {
  13. id: 1,
  14. name: 'X1 Carbon',
  15. ip_address: '192.168.1.100',
  16. serial_number: '00M09A350100001',
  17. access_code: '12345678',
  18. model: 'X1C',
  19. enabled: true,
  20. nozzle_diameter: 0.4,
  21. nozzle_type: 'hardened_steel',
  22. location: null,
  23. auto_archive: true,
  24. created_at: '2024-01-01T00:00:00Z',
  25. updated_at: '2024-01-01T00:00:00Z',
  26. },
  27. ];
  28. const mockPrinterStatus = {
  29. connected: true,
  30. state: 'IDLE',
  31. progress: 0,
  32. layer_num: 0,
  33. total_layers: 0,
  34. temperatures: { nozzle: 25, bed: 25, chamber: 25 },
  35. remaining_time: 0,
  36. filename: null,
  37. wifi_signal: -50,
  38. vt_tray: [],
  39. };
  40. describe('AddPrinterModal Discovery', () => {
  41. beforeEach(() => {
  42. server.use(
  43. http.get('/api/v1/printers/', () => {
  44. return HttpResponse.json(mockPrinters);
  45. }),
  46. http.get('/api/v1/printers/:id/status', () => {
  47. return HttpResponse.json(mockPrinterStatus);
  48. }),
  49. http.get('/api/v1/queue/', () => {
  50. return HttpResponse.json([]);
  51. })
  52. );
  53. });
  54. it('auto-populates subnet from discovery info in Docker mode', async () => {
  55. server.use(
  56. http.get('/api/v1/discovery/info', () => {
  57. return HttpResponse.json({
  58. is_docker: true,
  59. ssdp_running: false,
  60. scan_running: false,
  61. subnets: ['10.0.0.0/24'],
  62. });
  63. })
  64. );
  65. render(<PrintersPage />);
  66. // Wait for printer page to load
  67. await waitFor(() => {
  68. expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
  69. });
  70. // Click the Add Printer button
  71. const addButton = screen.getByText(/add printer/i);
  72. await userEvent.click(addButton);
  73. // Wait for the modal and discovery info to load
  74. await waitFor(() => {
  75. // Should show subnet dropdown with detected subnet
  76. const subnetSelect = screen.getByDisplayValue('10.0.0.0/24');
  77. expect(subnetSelect).toBeInTheDocument();
  78. });
  79. });
  80. it('shows dropdown when multiple subnets detected in Docker mode', async () => {
  81. server.use(
  82. http.get('/api/v1/discovery/info', () => {
  83. return HttpResponse.json({
  84. is_docker: true,
  85. ssdp_running: false,
  86. scan_running: false,
  87. subnets: ['192.168.1.0/24', '10.0.0.0/24'],
  88. });
  89. })
  90. );
  91. render(<PrintersPage />);
  92. await waitFor(() => {
  93. expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
  94. });
  95. const addButton = screen.getByText(/add printer/i);
  96. await userEvent.click(addButton);
  97. await waitFor(() => {
  98. // Should show a select element (dropdown) with both subnets and
  99. // the trailing "Custom subnet..." sentinel that lets a user enter
  100. // a CIDR for a printer on a different L3 segment (#1564).
  101. const selectElement = screen.getByDisplayValue('192.168.1.0/24');
  102. expect(selectElement.tagName).toBe('SELECT');
  103. const options = selectElement.querySelectorAll('option');
  104. expect(options).toHaveLength(3);
  105. expect(options[0].textContent).toBe('192.168.1.0/24');
  106. expect(options[1].textContent).toBe('10.0.0.0/24');
  107. expect(options[2].textContent).toMatch(/custom subnet/i);
  108. });
  109. });
  110. it('shows text input when no subnets detected in Docker mode', async () => {
  111. server.use(
  112. http.get('/api/v1/discovery/info', () => {
  113. return HttpResponse.json({
  114. is_docker: true,
  115. ssdp_running: false,
  116. scan_running: false,
  117. subnets: [],
  118. });
  119. })
  120. );
  121. render(<PrintersPage />);
  122. await waitFor(() => {
  123. expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
  124. });
  125. const addButton = screen.getByText(/add printer/i);
  126. await userEvent.click(addButton);
  127. await waitFor(() => {
  128. // Should show a text input with placeholder
  129. const textInput = screen.getByPlaceholderText('192.168.1.0/24');
  130. expect(textInput).toBeInTheDocument();
  131. expect(textInput.tagName).toBe('INPUT');
  132. });
  133. });
  134. it('shows the subnet picker in non-Docker mode too, with a Custom option (#1564)', async () => {
  135. // Pre-#1564 the subnet picker was gated on isDocker and a native
  136. // install only saw the "Discover Printers" button. SSDP doesn't
  137. // cross routers, so users with a printer on a different L3 segment
  138. // had no path to scan it. The picker is now always visible.
  139. server.use(
  140. http.get('/api/v1/discovery/info', () => {
  141. return HttpResponse.json({
  142. is_docker: false,
  143. ssdp_running: false,
  144. scan_running: false,
  145. subnets: ['192.168.1.0/24'],
  146. });
  147. })
  148. );
  149. render(<PrintersPage />);
  150. await waitFor(() => {
  151. expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
  152. });
  153. const addButton = screen.getByText(/add printer/i);
  154. await userEvent.click(addButton);
  155. // Detected subnet is the default value; "Custom subnet..." sits
  156. // alongside it so the user can pick a foreign CIDR.
  157. await waitFor(() => {
  158. const selectElement = screen.getByDisplayValue('192.168.1.0/24');
  159. expect(selectElement.tagName).toBe('SELECT');
  160. const options = selectElement.querySelectorAll('option');
  161. expect(options).toHaveLength(2);
  162. expect(options[0].textContent).toBe('192.168.1.0/24');
  163. expect(options[1].textContent).toMatch(/custom subnet/i);
  164. });
  165. // Default selection leaves the SSDP path in place — the button
  166. // still reads "Discover Printers on Network", not "Scan Subnet".
  167. expect(screen.getByText(/discover printers/i)).toBeInTheDocument();
  168. });
  169. });