PrintersPageBatchedReads.test.tsx 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * The Printers page reads every card's data with one request per kind of data.
  3. *
  4. * Each card used to ask for its own status, slot presets, AMS labels, plugs,
  5. * sensor readings and queue, so a farm's Printers page sent hundreds of
  6. * requests on load and the server worked through them one behind the other.
  7. * The cards still each run their own query; the API client batches them.
  8. */
  9. import { describe, it, expect, beforeEach } from 'vitest';
  10. import { waitFor } from '@testing-library/react';
  11. import { http, HttpResponse } from 'msw';
  12. import { server } from '../mocks/server';
  13. import { batchesSettled } from '../../api/batch';
  14. import { render } from '../utils';
  15. import { PrintersPage } from '../../pages/PrintersPage';
  16. const printer = (id: number) => ({
  17. id,
  18. name: `Printer ${id}`,
  19. ip_address: `192.168.1.${100 + id}`,
  20. serial_number: `01P00A00000000${id}`,
  21. access_code: '12345678',
  22. model: 'X1C',
  23. enabled: true,
  24. is_active: true,
  25. nozzle_diameter: 0.4,
  26. nozzle_type: 'stainless_steel',
  27. location: null,
  28. auto_archive: true,
  29. created_at: '2024-01-01T00:00:00Z',
  30. updated_at: '2024-01-01T00:00:00Z',
  31. });
  32. const status = (id: number) => ({
  33. id,
  34. name: `Printer ${id}`,
  35. connected: true,
  36. state: 'IDLE',
  37. progress: 0,
  38. layer_num: 0,
  39. total_layers: 0,
  40. temperatures: { nozzle: 25, bed: 25, chamber: 25 },
  41. remaining_time: 0,
  42. wifi_signal: -40,
  43. speed_level: 2,
  44. vt_tray: [],
  45. ams: [],
  46. });
  47. describe('PrintersPage — batched per-printer reads', () => {
  48. let paths: string[];
  49. beforeEach(() => {
  50. paths = [];
  51. server.events.removeAllListeners('request:start');
  52. server.events.on('request:start', ({ request }) => {
  53. // The default bulk mocks answer by asking the single routes; not the app's
  54. if (request.headers.get('x-test-fan-out')) return;
  55. const url = new URL(request.url);
  56. paths.push(url.pathname + url.search);
  57. });
  58. server.use(
  59. http.get('/api/v1/printers/', () => HttpResponse.json([printer(1), printer(2), printer(3)])),
  60. http.get('/api/v1/bulk/printer-statuses', () => HttpResponse.json([status(1), status(2), status(3)])),
  61. );
  62. });
  63. it('asks for every printer\'s status in one request', async () => {
  64. render(<PrintersPage />);
  65. await waitFor(() =>
  66. expect(paths.filter((p) => p.startsWith('/api/v1/bulk/printer-statuses'))).toHaveLength(1),
  67. );
  68. expect(paths).toContain('/api/v1/bulk/printer-statuses?ids=1,2,3');
  69. expect(paths.some((p) => /^\/api\/v1\/printers\/\d+\/status/.test(p))).toBe(false);
  70. });
  71. it('batches the other per-card reads as well', async () => {
  72. render(<PrintersPage />);
  73. await waitFor(() => {
  74. for (const route of ['slot-presets', 'ams-labels', 'card-plugs', 'ha-sensor-readings', 'printer-queues']) {
  75. expect(paths.some((p) => p.startsWith(`/api/v1/bulk/${route}?ids=1,2,3`))).toBe(true);
  76. }
  77. });
  78. // A bulk request that fails falls back to one request per printer, sent
  79. // after it: wait for those too, or they land after the check.
  80. await batchesSettled();
  81. const perPrinter = paths.filter((p) =>
  82. /^\/api\/v1\/(printers\/\d+\/(slot-presets|ams-labels)|smart-plugs\/by-printer\/\d+|ha-sensors\/by-printer\/\d+)/.test(p),
  83. );
  84. expect(perPrinter).toEqual([]);
  85. });
  86. });