| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- /**
- * Unit tests for the global search filter used in InventoryPage.
- *
- * The filter is a pure client-side computation — replicated inline here
- * following the same pattern as InventoryPageGrouping.test.ts so no DOM
- * render or API mock is needed.
- *
- * Fields covered: brand, material, color_name, subtype, note,
- * slicer_filament_name, storage_location.
- */
- import { describe, it, expect } from 'vitest';
- import type { InventorySpool } from '../../api/client';
- // Replicate the search filter from InventoryPage
- function applySearch(spools: InventorySpool[], search: string): InventorySpool[] {
- if (!search) return spools;
- const q = search.toLowerCase();
- return spools.filter((s) =>
- s.brand?.toLowerCase().includes(q) ||
- s.material.toLowerCase().includes(q) ||
- s.color_name?.toLowerCase().includes(q) ||
- s.subtype?.toLowerCase().includes(q) ||
- s.note?.toLowerCase().includes(q) ||
- s.slicer_filament_name?.toLowerCase().includes(q) ||
- s.storage_location?.toLowerCase().includes(q)
- );
- }
- function makeSpool(overrides: Partial<InventorySpool> & { id: number }): InventorySpool {
- return {
- material: 'PLA',
- subtype: 'Basic',
- brand: 'Bambu Lab',
- color_name: 'White',
- rgba: 'FFFFFFFF',
- label_weight: 1000,
- core_weight: 250,
- core_weight_catalog_id: null,
- weight_used: 0,
- weight_locked: false,
- slicer_filament: null,
- slicer_filament_name: null,
- nozzle_temp_min: null,
- nozzle_temp_max: null,
- note: null,
- added_full: null,
- last_used: null,
- encode_time: null,
- tag_uid: null,
- tray_uuid: null,
- data_origin: null,
- tag_type: null,
- archived_at: null,
- created_at: '2025-01-01T00:00:00Z',
- updated_at: '2025-01-01T00:00:00Z',
- k_profiles: [],
- cost_per_kg: null,
- last_scale_weight: null,
- last_weighed_at: null,
- storage_location: null,
- ...overrides,
- };
- }
- describe('InventoryPage search filter', () => {
- describe('storage_location', () => {
- it('returns spools whose storage_location matches the query', () => {
- const spools = [
- makeSpool({ id: 1, storage_location: 'IKEA Regal' }),
- makeSpool({ id: 2, storage_location: 'Kiste - PLA' }),
- makeSpool({ id: 3, storage_location: 'Lagerregal' }),
- ];
- expect(applySearch(spools, 'IKEA').map((s) => s.id)).toEqual([1]);
- expect(applySearch(spools, 'Kiste').map((s) => s.id)).toEqual([2]);
- expect(applySearch(spools, 'regal').map((s) => s.id)).toEqual([1, 3]);
- });
- it('is case-insensitive for storage_location', () => {
- const spools = [makeSpool({ id: 1, storage_location: 'IKEA Regal' })];
- expect(applySearch(spools, 'ikea regal')).toHaveLength(1);
- expect(applySearch(spools, 'IKEA REGAL')).toHaveLength(1);
- expect(applySearch(spools, 'ikEa')).toHaveLength(1);
- });
- it('matches partial storage_location strings', () => {
- const spools = [makeSpool({ id: 1, storage_location: 'Kiste - PLA' })];
- expect(applySearch(spools, 'Kis')).toHaveLength(1);
- expect(applySearch(spools, 'PLA')).toHaveLength(1);
- expect(applySearch(spools, '- PLA')).toHaveLength(1);
- });
- it('does not crash when storage_location is null', () => {
- const spools = [makeSpool({ id: 1, storage_location: null })];
- expect(() => applySearch(spools, 'regal')).not.toThrow();
- expect(applySearch(spools, 'regal')).toHaveLength(0);
- });
- it('excludes spools whose storage_location does not match', () => {
- const spools = [
- makeSpool({ id: 1, storage_location: 'IKEA Regal' }),
- makeSpool({ id: 2, storage_location: 'Kiste - PLA' }),
- ];
- expect(applySearch(spools, 'IKEA').map((s) => s.id)).toEqual([1]);
- });
- });
- describe('existing fields (regression)', () => {
- it('still finds by brand', () => {
- const spools = [
- makeSpool({ id: 1, brand: 'Bambu Lab' }),
- makeSpool({ id: 2, brand: 'Polymaker' }),
- ];
- expect(applySearch(spools, 'polymaker').map((s) => s.id)).toEqual([2]);
- });
- it('still finds by material', () => {
- const spools = [
- makeSpool({ id: 1, material: 'PLA' }),
- makeSpool({ id: 2, material: 'PETG' }),
- ];
- expect(applySearch(spools, 'petg').map((s) => s.id)).toEqual([2]);
- });
- it('still finds by color_name', () => {
- const spools = [
- makeSpool({ id: 1, color_name: 'Jade White' }),
- makeSpool({ id: 2, color_name: 'Black' }),
- ];
- expect(applySearch(spools, 'jade').map((s) => s.id)).toEqual([1]);
- });
- it('still finds by note', () => {
- const spools = [
- makeSpool({ id: 1, note: 'fast print only' }),
- makeSpool({ id: 2, note: null }),
- ];
- expect(applySearch(spools, 'fast').map((s) => s.id)).toEqual([1]);
- });
- it('returns all spools when search is empty', () => {
- const spools = [makeSpool({ id: 1 }), makeSpool({ id: 2 })];
- expect(applySearch(spools, '')).toHaveLength(2);
- });
- it('returns empty array when nothing matches', () => {
- const spools = [makeSpool({ id: 1, brand: 'Bambu Lab', material: 'PLA' })];
- expect(applySearch(spools, 'xxxxxxxx')).toHaveLength(0);
- });
- });
- describe('cross-field matching', () => {
- it('matches a spool if any field contains the query', () => {
- const spool = makeSpool({
- id: 1,
- brand: 'Bambu Lab',
- material: 'PLA',
- storage_location: 'IKEA Regal',
- });
- // Each individual field matches
- expect(applySearch([spool], 'Bambu')).toHaveLength(1);
- expect(applySearch([spool], 'PLA')).toHaveLength(1);
- expect(applySearch([spool], 'IKEA')).toHaveLength(1);
- });
- it('a query matching only storage_location is found even when other fields do not match', () => {
- const spools = [
- makeSpool({ id: 1, brand: 'Polymaker', material: 'PETG', storage_location: 'IKEA Regal' }),
- makeSpool({ id: 2, brand: 'Polymaker', material: 'PETG', storage_location: 'Kiste' }),
- ];
- const result = applySearch(spools, 'IKEA');
- expect(result.map((s) => s.id)).toEqual([1]);
- });
- });
- });
|