PrintersPageDryingBadge.test.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * The active-cycle badge on the AMS card (#2759).
  3. *
  4. * Bambu never echoes back which filament or temperature a drying cycle is
  5. * running, so the backend hands us two independent fields: `dry_filament`,
  6. * which it can also infer from a uniformly loaded unit, and `dry_target_temp`,
  7. * which it only knows from the target it cached when sending the command. The
  8. * temperature can therefore go missing while the filament survives, and the
  9. * badge has to render that pairing rather than dropping both.
  10. *
  11. * The reporter's second AMS held only PLA and was drying at the 45°C they
  12. * picked; with no cached target the badge previously showed the RFID
  13. * recommendation and read "PLA @ 55°C".
  14. */
  15. import { describe, it, expect, beforeEach } from 'vitest';
  16. import { screen, waitFor } from '@testing-library/react';
  17. import { render } from '../utils';
  18. import { PrintersPage } from '../../pages/PrintersPage';
  19. import { http, HttpResponse } from 'msw';
  20. import { server } from '../mocks/server';
  21. const mockPrinter = {
  22. id: 1,
  23. name: 'X1C',
  24. ip_address: '192.168.1.100',
  25. serial_number: '01P00A000000001',
  26. access_code: '12345678',
  27. model: 'X1C',
  28. enabled: true,
  29. nozzle_diameter: 0.4,
  30. nozzle_type: 'stainless_steel',
  31. location: 'Workshop',
  32. auto_archive: true,
  33. created_at: '2024-01-01T00:00:00Z',
  34. updated_at: '2024-01-01T00:00:00Z',
  35. };
  36. const baseTray = {
  37. tray_color: 'FF0000FF',
  38. tray_type: 'PLA',
  39. tray_sub_brands: 'PLA Basic',
  40. tray_id_name: 'A00-R0',
  41. tray_info_idx: 'GFA00',
  42. remain: 80,
  43. k: 0.02,
  44. cali_idx: null,
  45. tag_uid: null,
  46. tray_uuid: null,
  47. nozzle_temp_min: 190,
  48. nozzle_temp_max: 230,
  49. drying_temp: 55,
  50. drying_time: 8,
  51. state: 3,
  52. };
  53. /** An AMS 2 Pro twelve hours into a cycle, with the badge fields under test. */
  54. function makeStatus(target: { dry_filament: string | null; dry_target_temp: number | null }) {
  55. return {
  56. connected: true,
  57. state: 'IDLE',
  58. progress: 0,
  59. layer_num: 0,
  60. total_layers: 0,
  61. temperatures: { nozzle: 25, bed: 25, chamber: 25 },
  62. remaining_time: 0,
  63. filename: null,
  64. wifi_signal: -29,
  65. speed_level: 2,
  66. supports_drying: true,
  67. drying_screen_only: false,
  68. vt_tray: [],
  69. ams: [
  70. {
  71. id: 0,
  72. humidity: 30,
  73. temp: 33,
  74. is_ams_ht: false,
  75. serial_number: 'AMS00',
  76. sw_ver: '03.00.21.29',
  77. dry_time: 719,
  78. dry_status: 2,
  79. dry_sub_status: 0,
  80. dry_sf_reason: [],
  81. module_type: 'n3f',
  82. ...target,
  83. tray: [0, 1, 2, 3].map((id) => ({ id, ...baseTray })),
  84. },
  85. ],
  86. };
  87. }
  88. function renderWith(target: { dry_filament: string | null; dry_target_temp: number | null }) {
  89. server.use(
  90. http.get('/api/v1/printers/', () => HttpResponse.json([mockPrinter])),
  91. http.get('/api/v1/printers/:id/status', () => HttpResponse.json(makeStatus(target))),
  92. http.get('/api/v1/queue/', () => HttpResponse.json([])),
  93. );
  94. render(<PrintersPage />);
  95. }
  96. describe('PrintersPage — AMS drying badge (#2759)', () => {
  97. beforeEach(() => {
  98. server.use(http.get('/api/v1/queue/', () => HttpResponse.json([])));
  99. });
  100. it('names the filament and the temperature when the cycle target is known', async () => {
  101. renderWith({ dry_filament: 'PLA', dry_target_temp: 45 });
  102. await waitFor(() => {
  103. expect(screen.getAllByText('PLA @ 45°C').length).toBeGreaterThan(0);
  104. });
  105. });
  106. it('still names the filament when only the temperature is unknown', async () => {
  107. renderWith({ dry_filament: 'PLA', dry_target_temp: null });
  108. // The filament survives on its own — dropping it too would leave the badge
  109. // showing a bare countdown for a cycle we can still describe.
  110. await waitFor(() => {
  111. expect(screen.getAllByText('PLA').length).toBeGreaterThan(0);
  112. });
  113. // And it must not fall back to the trays' RFID recommendation (55°C here),
  114. // which is what the user's chosen 45°C was being overwritten with. Scoped
  115. // to the badge's own "<filament> @ <temp>°C" shape — the card carries
  116. // unrelated nozzle and bed readings in °C.
  117. expect(screen.queryByText(/@ \d+°C/)).toBeNull();
  118. });
  119. it('shows the countdown alone when the unit gives no evidence at all', async () => {
  120. renderWith({ dry_filament: null, dry_target_temp: null });
  121. await waitFor(() => {
  122. expect(screen.getAllByText(/11h 59m/).length).toBeGreaterThan(0);
  123. });
  124. expect(screen.queryByText(/@ \d+°C/)).toBeNull();
  125. });
  126. });