FilamentMappingSlotCost.test.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. /**
  2. * The print dialog prices each slot at the rate its print is charged.
  3. *
  4. * The estimate and the per-slot "g left" came from the built-in assignment
  5. * table in both inventory modes. In Spoolman mode that table holds nothing, or
  6. * rows from before the switch, so a Spoolman print was estimated at the
  7. * default rate or at a spool the printer is not drawing on. Both now come from
  8. * the mode-aware slot payload (`/printers/{id}/inventory-remain`).
  9. */
  10. import { describe, it, expect, afterEach, vi } from 'vitest';
  11. import { screen, waitFor, cleanup, fireEvent } from '@testing-library/react';
  12. import { http, HttpResponse } from 'msw';
  13. import { render } from '../utils';
  14. import { server } from '../mocks/server';
  15. import { FilamentMapping } from '../../components/PrintModal/FilamentMapping';
  16. const status = {
  17. id: 1,
  18. name: 'X1C',
  19. connected: true,
  20. state: 'IDLE',
  21. ams: [{ id: 0, tray: [{ id: 0, tray_type: 'PLA', tray_color: 'FF0000', tray_info_idx: 'GFA00' }] }],
  22. vt_tray: [],
  23. ams_extruder_map: {},
  24. };
  25. const slot = (cost: number | null) => ({
  26. ams_id: 0,
  27. tray_id: 0,
  28. global_tray_id: 0,
  29. material_key: 'k',
  30. remaining_g: 312.4,
  31. extruder: 0,
  32. spool: null,
  33. cost_per_kg: cost,
  34. });
  35. // A row left in the built-in table from before the switch to Spoolman.
  36. const staleBuiltIn = [
  37. { id: 1, spool_id: 9, printer_id: 1, ams_id: 0, tray_id: 0, spool: { cost_per_kg: 99, label_weight: 1000, weight_used: 0 } },
  38. ];
  39. function renderWith(cost: number | null) {
  40. const onCost = vi.fn();
  41. server.use(
  42. http.get('/api/v1/printers/:id/status', () => HttpResponse.json(status)),
  43. http.get('/api/v1/inventory/assignments', () => HttpResponse.json(staleBuiltIn)),
  44. http.get('/api/v1/printers/:id/inventory-remain', () =>
  45. HttpResponse.json({ inventory_remain_g: { '0': 312.4 }, slot_materials: [slot(cost)] }),
  46. ),
  47. );
  48. render(
  49. <FilamentMapping
  50. printerId={1}
  51. filamentReqs={{ filaments: [{ slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 100, used_meters: 30 }] }}
  52. manualMappings={{}}
  53. onManualMappingChange={() => {}}
  54. currencySymbol="$"
  55. defaultCostPerKg={25}
  56. onEstimatedCostChange={onCost}
  57. defaultExpanded
  58. />,
  59. );
  60. return onCost;
  61. }
  62. afterEach(() => {
  63. cleanup();
  64. vi.clearAllMocks();
  65. });
  66. describe('FilamentMapping slot price', () => {
  67. it("estimates at the bound spool's rate, not a stale built-in row's", async () => {
  68. const onCost = renderWith(40);
  69. await waitFor(() => expect(onCost).toHaveBeenLastCalledWith(4));
  70. expect(screen.getByText('$4.00')).toBeInTheDocument();
  71. });
  72. it('estimates an unpriced spool at the default rate', async () => {
  73. const onCost = renderWith(null);
  74. await waitFor(() => expect(onCost).toHaveBeenLastCalledWith(2.5));
  75. });
  76. it("offers the slot with the bound spool's grams left", async () => {
  77. renderWith(40);
  78. const trigger = await waitFor(() => screen.getAllByRole('combobox')[0]);
  79. fireEvent.click(trigger);
  80. const listbox = await waitFor(() => screen.getByRole('listbox'));
  81. expect(listbox.textContent).toContain('312g left');
  82. expect(listbox.textContent).not.toContain('1000g left');
  83. });
  84. });