VariantCandidates.test.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /**
  2. * Cross-model candidate list (#671).
  3. *
  4. * The list carries the one decision the user makes that the scheduler cannot:
  5. * which printer they would rather have when more than one is free. Order is
  6. * that decision, so it has to be visible and editable.
  7. */
  8. import { describe, it, expect, vi, beforeEach } from 'vitest';
  9. import { screen, waitFor } from '@testing-library/react';
  10. import userEvent from '@testing-library/user-event';
  11. import { render } from '../utils';
  12. import { VariantCandidates, type VariantCandidate } from '../../components/PrintModal/VariantCandidates';
  13. import { api } from '../../api/client';
  14. vi.mock('../../api/client', async () => {
  15. const actual = await vi.importActual<typeof import('../../api/client')>('../../api/client');
  16. return {
  17. ...actual,
  18. api: { ...actual.api, getLibraryFilePlates: vi.fn() },
  19. };
  20. });
  21. const CANDIDATES: VariantCandidate[] = [
  22. { id: 1, filename: 'bracket_h2s.gcode.3mf', sliced_for_model: 'H2S' },
  23. { id: 2, filename: 'bracket_h2c.gcode.3mf', sliced_for_model: 'H2C' },
  24. ];
  25. function setup(overrides: Partial<React.ComponentProps<typeof VariantCandidates>> = {}) {
  26. const onReorder = vi.fn();
  27. const onPlateChange = vi.fn();
  28. render(
  29. <VariantCandidates
  30. candidates={CANDIDATES}
  31. onReorder={onReorder}
  32. plateByFile={{}}
  33. onPlateChange={onPlateChange}
  34. {...overrides}
  35. />,
  36. );
  37. return { onReorder, onPlateChange };
  38. }
  39. describe('VariantCandidates', () => {
  40. beforeEach(() => {
  41. vi.mocked(api.getLibraryFilePlates).mockResolvedValue({
  42. file_id: 1,
  43. filename: 'x',
  44. plates: [],
  45. is_multi_plate: false,
  46. });
  47. });
  48. it('lists every candidate with the model its file was sliced for', async () => {
  49. setup();
  50. expect(await screen.findByText('bracket_h2s.gcode.3mf')).toBeInTheDocument();
  51. expect(screen.getByText('bracket_h2c.gcode.3mf')).toBeInTheDocument();
  52. expect(screen.getByText('H2S')).toBeInTheDocument();
  53. expect(screen.getByText('H2C')).toBeInTheDocument();
  54. });
  55. it('moves a candidate down, which is how priority is expressed', async () => {
  56. const user = userEvent.setup();
  57. const { onReorder } = setup();
  58. const downButtons = await screen.findAllByLabelText('Move down');
  59. await user.click(downButtons[0]);
  60. expect(onReorder).toHaveBeenCalledWith([CANDIDATES[1], CANDIDATES[0]]);
  61. });
  62. it('cannot move the first candidate up or the last one down', async () => {
  63. setup();
  64. const up = await screen.findAllByLabelText('Move up');
  65. const down = await screen.findAllByLabelText('Move down');
  66. expect(up[0]).toBeDisabled();
  67. expect(down[down.length - 1]).toBeDisabled();
  68. });
  69. it('offers a plate picker only for the candidates that have several plates', async () => {
  70. vi.mocked(api.getLibraryFilePlates).mockImplementation(async (fileId: number) =>
  71. fileId === 2
  72. ? {
  73. file_id: 2,
  74. filename: 'bracket_h2c.gcode.3mf',
  75. is_multi_plate: true,
  76. plates: [
  77. { index: 1, name: 'Plate 1', objects: [], has_thumbnail: false, thumbnail_url: null, print_time_seconds: null, filament_used_grams: null, filaments: [] },
  78. { index: 2, name: 'Plate 2', objects: [], has_thumbnail: false, thumbnail_url: null, print_time_seconds: null, filament_used_grams: null, filaments: [] },
  79. ],
  80. }
  81. : { file_id: fileId, filename: 'x', is_multi_plate: false, plates: [] },
  82. );
  83. setup();
  84. // One picker, for the multi-plate file only — a single-plate candidate has
  85. // nothing to choose and the control would just be noise.
  86. await waitFor(() => expect(screen.getAllByRole('combobox')).toHaveLength(1));
  87. expect(screen.getByLabelText('Plate for bracket_h2c.gcode.3mf')).toBeInTheDocument();
  88. });
  89. it('reports the chosen plate against the file it belongs to', async () => {
  90. const user = userEvent.setup();
  91. vi.mocked(api.getLibraryFilePlates).mockImplementation(async (fileId: number) => ({
  92. file_id: fileId,
  93. filename: 'x',
  94. is_multi_plate: true,
  95. plates: [
  96. { index: 1, name: 'Plate 1', objects: [], has_thumbnail: false, thumbnail_url: null, print_time_seconds: null, filament_used_grams: null, filaments: [] },
  97. { index: 2, name: 'Plate 2', objects: [], has_thumbnail: false, thumbnail_url: null, print_time_seconds: null, filament_used_grams: null, filaments: [] },
  98. ],
  99. }));
  100. const { onPlateChange } = setup();
  101. const pickers = await screen.findAllByRole('combobox');
  102. await user.selectOptions(pickers[1], '2');
  103. expect(onPlateChange).toHaveBeenCalledWith(2, 2);
  104. });
  105. });