PrintersPageCompactMetrics.test.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * Tests for the metrics line on the size-S (compact) printer card (#2674).
  3. *
  4. * Size S used to show a name, a connection pip and a progress bar — not enough
  5. * to answer "which printer finishes first", which is the whole point of a
  6. * wall-mounted fleet view. It now carries remaining time, ETA and layer
  7. * progress, reusing the formatters the expanded card already uses.
  8. */
  9. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
  10. import { screen } from '@testing-library/react';
  11. import { render } from '../utils';
  12. import { PrintersPage } from '../../pages/PrintersPage';
  13. import { http, HttpResponse } from 'msw';
  14. import { server } from '../mocks/server';
  15. const mockPrinters = [
  16. {
  17. id: 1,
  18. name: 'X1 Carbon',
  19. ip_address: '192.168.1.100',
  20. serial_number: '00M09A350100001',
  21. access_code: '12345678',
  22. model: 'X1C',
  23. enabled: true,
  24. nozzle_diameter: 0.4,
  25. nozzle_type: 'hardened_steel',
  26. location: 'Workshop',
  27. auto_archive: true,
  28. is_active: true,
  29. created_at: '2024-01-01T00:00:00Z',
  30. updated_at: '2024-01-01T00:00:00Z',
  31. },
  32. ];
  33. const baseStatus = {
  34. connected: true,
  35. temperatures: { nozzle: 220, bed: 60, chamber: 35 },
  36. filename: 'test_print.3mf',
  37. wifi_signal: -50,
  38. vt_tray: [],
  39. speed_level: 2,
  40. hms_errors: [],
  41. };
  42. // 83 minutes remaining → formatDuration(83 * 60) === "1h 23m"
  43. const printingStatus = {
  44. ...baseStatus,
  45. state: 'RUNNING',
  46. progress: 42,
  47. layer_num: 120,
  48. total_layers: 267,
  49. remaining_time: 83,
  50. };
  51. const idleStatus = {
  52. ...baseStatus,
  53. state: 'IDLE',
  54. progress: 0,
  55. layer_num: 0,
  56. total_layers: 0,
  57. remaining_time: 0,
  58. temperatures: { nozzle: 25, bed: 25, chamber: 25 },
  59. };
  60. function useStatus(status: Record<string, unknown>) {
  61. server.use(
  62. http.get('/api/v1/printers/', () => HttpResponse.json(mockPrinters)),
  63. http.get('/api/v1/queue/', () => HttpResponse.json([])),
  64. http.get('/api/v1/printers/:id/status', () => HttpResponse.json(status)),
  65. );
  66. }
  67. describe('PrintersPage — size S metrics line (#2674)', () => {
  68. beforeEach(() => {
  69. // Size S. The card size is read from localStorage on first render — and
  70. // the test harness replaces localStorage with bare vi.fn() stubs
  71. // (setup.ts), so setItem is a no-op and getItem has to be taught the
  72. // value. Without this the page falls back to its size-M default and the
  73. // compact branch never renders.
  74. vi.mocked(localStorage.getItem).mockImplementation((key: string) =>
  75. key === 'printerCardSize' ? '1' : null,
  76. );
  77. });
  78. afterEach(() => {
  79. vi.mocked(localStorage.getItem).mockReset();
  80. });
  81. it('shows remaining time, ETA and layer progress while printing', async () => {
  82. useStatus(printingStatus);
  83. render(<PrintersPage />);
  84. expect(await screen.findByText('1h 23m')).toBeInTheDocument();
  85. expect(screen.getByText('120/267')).toBeInTheDocument();
  86. // ETA is rendered as "ETA <clock time>"; the clock time itself depends on
  87. // when the test runs, so match the prefix.
  88. expect(screen.getByText(/^ETA\s/)).toBeInTheDocument();
  89. });
  90. it('shows no metrics when the printer is idle', async () => {
  91. useStatus(idleStatus);
  92. render(<PrintersPage />);
  93. // The progress placeholder confirms we are looking at the compact card.
  94. expect(await screen.findByText('---%')).toBeInTheDocument();
  95. expect(screen.queryByText(/^ETA\s/)).not.toBeInTheDocument();
  96. expect(screen.queryByText('0/0')).not.toBeInTheDocument();
  97. });
  98. it('omits the ETA but keeps layers when the printer reports no remaining time', async () => {
  99. useStatus({ ...printingStatus, remaining_time: 0 });
  100. render(<PrintersPage />);
  101. expect(await screen.findByText('120/267')).toBeInTheDocument();
  102. expect(screen.queryByText(/^ETA\s/)).not.toBeInTheDocument();
  103. });
  104. it('omits layers when the printer reports no total layer count', async () => {
  105. useStatus({ ...printingStatus, layer_num: 0, total_layers: 0 });
  106. render(<PrintersPage />);
  107. expect(await screen.findByText('1h 23m')).toBeInTheDocument();
  108. expect(screen.queryByText('0/0')).not.toBeInTheDocument();
  109. });
  110. });