Files
bambuddy/frontend/src/__tests__/utils/inventorySearch.test.ts
T
maziggy 5c17cd4973 fix(inventory): restore Spoolman spool ID search + Unassign button (#1336)
Two regressions reported in #1336:

  1. spoolMatchesQuery did not include spool.id in the predicate, so
     typing a numeric Spoolman ID into the Assign Spool dialog or the
     Inventory page search returned no matches. Predicate now also
     tests String(spool.id).includes(q).

  2. The Unassign button in the spool edit modal was permanently
     disabled for Spoolman-mode spools. The modal only ever queried
     the legacy spool_assignments table (keyed by spool_id), but in
     Spoolman mode the assignment lives in spoolman_slot_assignments
     (keyed by spoolman_spool_id). Both the lookup query and the
     unassign mutation now branch on the spoolmanMode prop and call
     the Spoolman-flavored endpoints.
2026-05-14 14:19:12 +02:00

118 lines
3.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { spoolMatchesQuery, filterSpoolsByQuery } from '../../utils/inventorySearch';
import type { InventorySpool } from '../../api/client';
function makeSpool(overrides: Partial<InventorySpool> = {}): InventorySpool {
return {
id: 1,
material: 'PLA',
subtype: 'Basic',
color_name: 'Red',
rgba: 'FF0000FF',
brand: 'Bambu Lab',
label_weight: 1000,
core_weight: 250,
core_weight_catalog_id: null,
weight_used: 0,
slicer_filament: null,
slicer_filament_name: null,
nozzle_temp_min: 220,
nozzle_temp_max: 240,
note: null,
added_full: null,
last_used: null,
encode_time: null,
tag_uid: null,
tray_uuid: null,
data_origin: 'local',
tag_type: null,
archived_at: null,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
cost_per_kg: null,
last_scale_weight: null,
last_weighed_at: null,
category: null,
low_stock_threshold_pct: null,
...overrides,
};
}
describe('spoolMatchesQuery', () => {
it('returns true for empty query', () => {
expect(spoolMatchesQuery(makeSpool(), '')).toBe(true);
});
it('matches on material (case-insensitive)', () => {
const spool = makeSpool({ material: 'PETG' });
expect(spoolMatchesQuery(spool, 'petg')).toBe(true);
expect(spoolMatchesQuery(spool, 'PET')).toBe(true);
expect(spoolMatchesQuery(spool, 'pla')).toBe(false);
});
it('matches on brand (case-insensitive)', () => {
const spool = makeSpool({ brand: 'Prusament' });
expect(spoolMatchesQuery(spool, 'prusa')).toBe(true);
expect(spoolMatchesQuery(spool, 'PRUSA')).toBe(true);
});
it('matches on color_name (case-insensitive)', () => {
const spool = makeSpool({ color_name: 'Galaxy Black' });
expect(spoolMatchesQuery(spool, 'galaxy')).toBe(true);
expect(spoolMatchesQuery(spool, 'GALAXY')).toBe(true);
expect(spoolMatchesQuery(spool, 'blue')).toBe(false);
});
it('matches on subtype (case-insensitive)', () => {
const spool = makeSpool({ subtype: 'Matte' });
expect(spoolMatchesQuery(spool, 'matt')).toBe(true);
expect(spoolMatchesQuery(spool, 'MATTE')).toBe(true);
});
it('returns false when null optional fields do not match', () => {
const spool = makeSpool({ brand: null, color_name: null, subtype: null });
expect(spoolMatchesQuery(spool, 'bambu')).toBe(false);
});
it('matches on numeric id (#1336)', () => {
const spool = makeSpool({ id: 42, material: 'PLA', brand: null, color_name: null, subtype: null, note: null });
expect(spoolMatchesQuery(spool, '42')).toBe(true);
expect(spoolMatchesQuery(spool, '4')).toBe(true);
expect(spoolMatchesQuery(spool, '99')).toBe(false);
});
});
describe('filterSpoolsByQuery', () => {
const spools = [
makeSpool({ id: 1, material: 'PLA', brand: 'Bambu Lab', color_name: 'Red' }),
makeSpool({ id: 2, material: 'PETG', brand: 'Prusament', color_name: 'Blue' }),
makeSpool({ id: 3, material: 'ABS', brand: null, color_name: 'Black', subtype: 'Matte' }),
];
it('returns all spools for empty query', () => {
expect(filterSpoolsByQuery(spools, '')).toHaveLength(3);
});
it('filters by material', () => {
const result = filterSpoolsByQuery(spools, 'pla');
expect(result).toHaveLength(1);
expect(result[0].id).toBe(1);
});
it('filters by brand', () => {
const result = filterSpoolsByQuery(spools, 'prusa');
expect(result).toHaveLength(1);
expect(result[0].id).toBe(2);
});
it('filters by subtype', () => {
const result = filterSpoolsByQuery(spools, 'matte');
expect(result).toHaveLength(1);
expect(result[0].id).toBe(3);
});
it('returns empty array when no match', () => {
expect(filterSpoolsByQuery(spools, 'nylon')).toHaveLength(0);
});
});