| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- /**
- * Tests for the FilamentOverride component.
- *
- * FilamentOverride allows users to override the 3MF's original filament
- * choices with filaments available across printers of the selected model.
- */
- import { describe, it, expect, vi, afterEach } from 'vitest';
- import { screen, fireEvent, cleanup } from '@testing-library/react';
- import { render } from '../utils';
- import { FilamentOverride } from '../../components/PrintModal/FilamentOverride';
- import type { FilamentReqsData } from '../../components/PrintModal/types';
- const defaultFilamentReqs: FilamentReqsData = {
- filaments: [
- { slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 25, used_meters: 8.5 },
- ],
- };
- const defaultAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PLA', color: '#00FF00', tray_info_idx: 'GFA01', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PETG', color: '#0000FF', tray_info_idx: 'GFG00', tray_sub_brands: 'PETG Basic', extruder_id: null },
- ];
- const mockOnChange = vi.fn();
- afterEach(() => {
- cleanup();
- vi.clearAllMocks();
- });
- describe('FilamentOverride', () => {
- describe('rendering', () => {
- it('returns null when filamentReqs is undefined', () => {
- render(
- <FilamentOverride
- filamentReqs={undefined}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- expect(screen.queryByText('Filament Override')).not.toBeInTheDocument();
- });
- it('returns null when filaments array is empty', () => {
- render(
- <FilamentOverride
- filamentReqs={{ filaments: [] }}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- expect(screen.queryByText('Filament Override')).not.toBeInTheDocument();
- });
- it('returns null when availableFilaments is empty', () => {
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={[]}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- expect(screen.queryByText('Filament Override')).not.toBeInTheDocument();
- });
- it('renders filament slot with type and grams', () => {
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- // The grams text "(25g)" is in a nested span within the type label
- expect(screen.getByText('(25g)')).toBeInTheDocument();
- // "Filament Override" heading confirms the section renders
- expect(screen.getByText('Filament Override')).toBeInTheDocument();
- });
- it('renders override dropdown for each slot', () => {
- const twoSlotReqs: FilamentReqsData = {
- filaments: [
- { slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 25, used_meters: 8.5 },
- { slot_id: 2, type: 'PLA', color: '#00FF00', used_grams: 10, used_meters: 3.2 },
- ],
- };
- render(
- <FilamentOverride
- filamentReqs={twoSlotReqs}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const selects = screen.getAllByRole('combobox');
- expect(selects).toHaveLength(2);
- });
- });
- describe('type filtering', () => {
- it('only shows same-type filaments in dropdown', () => {
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = select.querySelectorAll('option');
- // 1 default "Original" option + 2 PLA options (not PETG)
- expect(options).toHaveLength(3);
- // Verify no PETG option values exist
- const optionValues = Array.from(options).map((o) => o.getAttribute('value'));
- expect(optionValues).not.toContain('PETG|#0000FF');
- });
- it('shows all same-type options regardless of color', () => {
- const threeColorAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PLA', color: '#00FF00', tray_info_idx: 'GFA01', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PLA', color: '#FFFFFF', tray_info_idx: 'GFA02', tray_sub_brands: 'PLA Basic', extruder_id: null },
- ];
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={threeColorAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = select.querySelectorAll('option');
- // 1 default "Original" option + 3 PLA color options
- expect(options).toHaveLength(4);
- });
- });
- describe('subtype display', () => {
- it('shows tray_sub_brands in dropdown options when available', () => {
- const subtypeAvailable = [
- { type: 'PLA', color: '#000000', tray_info_idx: 'GFL99', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PLA', color: '#000000', tray_info_idx: 'GFL05', tray_sub_brands: 'PLA Matte', extruder_id: null },
- ];
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={subtypeAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = Array.from(select.querySelectorAll('option'));
- const optionTexts = options.map((o) => o.textContent);
- // Should show "PLA Basic" and "PLA Matte", not just "PLA"
- expect(optionTexts.some((t) => t?.includes('PLA Basic'))).toBe(true);
- expect(optionTexts.some((t) => t?.includes('PLA Matte'))).toBe(true);
- });
- it('falls back to type when tray_sub_brands is empty', () => {
- const noSubtypeAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: '', extruder_id: null },
- ];
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={noSubtypeAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = Array.from(select.querySelectorAll('option'));
- // Non-default option should show "PLA" as the type fallback
- const nonDefaultOptions = options.filter((o) => o.getAttribute('value') !== '');
- expect(nonDefaultOptions[0].textContent).toContain('PLA');
- });
- });
- describe('nozzle filtering', () => {
- it('filters by extruder_id when nozzle_id is set', () => {
- const nozzleReqs: FilamentReqsData = {
- filaments: [
- { slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 25, used_meters: 8.5, nozzle_id: 0 },
- ],
- };
- const dualExtruderAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', extruder_id: 0 },
- { type: 'PLA', color: '#00FF00', tray_info_idx: 'GFA01', tray_sub_brands: 'PLA Basic', extruder_id: 1 },
- ];
- render(
- <FilamentOverride
- filamentReqs={nozzleReqs}
- availableFilaments={dualExtruderAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = select.querySelectorAll('option');
- // 1 default + 1 PLA with extruder_id=0 (extruder_id=1 is filtered out)
- expect(options).toHaveLength(2);
- const optionValues = Array.from(options).map((o) => o.getAttribute('value'));
- expect(optionValues).toContain('PLA|#FF0000');
- expect(optionValues).not.toContain('PLA|#00FF00');
- });
- it('shows all filaments when nozzle_id is undefined', () => {
- const noNozzleReqs: FilamentReqsData = {
- filaments: [
- { slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 25, used_meters: 8.5 },
- ],
- };
- const mixedExtruderAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', extruder_id: 0 },
- { type: 'PLA', color: '#00FF00', tray_info_idx: 'GFA01', tray_sub_brands: 'PLA Basic', extruder_id: 1 },
- ];
- render(
- <FilamentOverride
- filamentReqs={noNozzleReqs}
- availableFilaments={mixedExtruderAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = select.querySelectorAll('option');
- // 1 default + 2 PLA options (no nozzle filtering)
- expect(options).toHaveLength(3);
- });
- it('includes filaments with null extruder_id', () => {
- const nozzleReqs: FilamentReqsData = {
- filaments: [
- { slot_id: 1, type: 'PLA', color: '#FF0000', used_grams: 25, used_meters: 8.5, nozzle_id: 0 },
- ],
- };
- const mixedAvailable = [
- { type: 'PLA', color: '#FF0000', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', extruder_id: 0 },
- { type: 'PLA', color: '#00FF00', tray_info_idx: 'GFA01', tray_sub_brands: 'PLA Basic', extruder_id: null },
- { type: 'PLA', color: '#FFFFFF', tray_info_idx: 'GFA02', tray_sub_brands: 'PLA Basic', extruder_id: 1 },
- ];
- render(
- <FilamentOverride
- filamentReqs={nozzleReqs}
- availableFilaments={mixedAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- const options = select.querySelectorAll('option');
- // 1 default + extruder_id=0 + extruder_id=null (extruder_id=1 filtered out)
- expect(options).toHaveLength(3);
- const optionValues = Array.from(options).map((o) => o.getAttribute('value'));
- expect(optionValues).toContain('PLA|#FF0000');
- expect(optionValues).toContain('PLA|#00FF00');
- expect(optionValues).not.toContain('PLA|#FFFFFF');
- });
- });
- describe('interactions', () => {
- it('calls onChange when selecting an override', () => {
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={defaultAvailable}
- overrides={{}}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- fireEvent.change(select, { target: { value: 'PLA|#00FF00' } });
- expect(mockOnChange).toHaveBeenCalledWith({
- 1: { type: 'PLA', color: '#00FF00' },
- });
- });
- it('calls onChange to remove override when selecting original', () => {
- const activeOverrides = {
- 1: { type: 'PLA', color: '#00FF00' },
- };
- render(
- <FilamentOverride
- filamentReqs={defaultFilamentReqs}
- availableFilaments={defaultAvailable}
- overrides={activeOverrides}
- onChange={mockOnChange}
- />
- );
- const select = screen.getByRole('combobox');
- fireEvent.change(select, { target: { value: '' } });
- expect(mockOnChange).toHaveBeenCalledWith({});
- });
- });
- });
|