import { describe, expect, it } from 'vitest'; import { defaultProject, rectangle } from '../src/lib/defaults'; import { focusLabel, findBoardMatches } from '../src/lib/board-tracking'; import { cutData } from '../src/lib/cut-list'; import type { FlooringPiece, FlooringPlan, SkirtingPiece, SkirtingPlan } from '../src/lib/types'; const project = defaultProject(); project.rooms.push(rectangle('room-2', 'Office')); function floorPiece(id: string, stockId: string, roomId = 'room-1', cut = true): FlooringPiece { const length = cut ? 400 : 1200; return { id, stockId, roomId, cut, length, width: 190, blankLength: length, blankWidth: 190, row: 1, polygon: [], template: [ { x: 0, y: 0 }, { x: length, y: 0 }, { x: length, y: 190 }, { x: 0, y: 190 }, ], shaped: false, reused: false, factoryLeft: false, factoryRight: false, ripEdge: null, }; } const flooring: FlooringPlan = { pieces: [ floorPiece('F1', 'B1'), floorPiece('F12', 'B1', 'room-2'), floorPiece('F10', 'B10'), floorPiece('FU1', 'B3', 'room-2', false), ], rows: [], stocks: [ { id: 'B1', pieceIds: ['F1', 'F12'], remaining: 394 }, { id: 'B10', pieceIds: ['F10'], remaining: 797 }, { id: 'B3', pieceIds: ['FU1'], remaining: 0 }, ], boardCount: 3, cuts: 3, wasteArea: 0, wastePercent: 0, reusedCount: 0, coveredArea: 0, issues: [], }; function skirtPiece(id: string, stockId: string, roomId = 'room-1', length = 900): SkirtingPiece { const template: SkirtingPiece['template'] = [ { x: 0, y: 0 }, { x: length, y: 0 }, { x: length, y: 18 }, { x: 0, y: 18 }, ]; return { id, stockId, roomId, length, blankLength: length, cut: length !== 2400, backLength: length, wallId: 'w1', wallIndex: 0, start: { x: 0, y: 0 }, end: { x: length, y: 0 }, leftMitre: 0, rightMitre: 0, leftType: 'square', rightType: 'square', template, installedTemplate: template, visibleLength: length, }; } const skirting: SkirtingPlan = { pieces: [ skirtPiece('A2', 'S6'), skirtPiece('B1', 'S6', 'room-2', 1497), skirtPiece('B10', 'S60'), skirtPiece('SU1', 'S7', 'room-2', 2400), ], stocks: [ { id: 'S6', length: 2400, used: 2397, waste: 3, pieceIds: ['A2', 'B1'] }, { id: 'S60', length: 2400, used: 900, waste: 1500, pieceIds: ['B10'] }, { id: 'S7', length: 2400, used: 2400, waste: 0, pieceIds: ['SU1'] }, ], stockCount: 3, cuts: 3, waste: 1503, issues: [], }; const find = (query: string) => findBoardMatches(project, flooring, skirting, query); const cuts = cutData(flooring, skirting, project.flooring); describe('board and cut location', () => { it('matches complete cut codes and multiple codes without interpreting stock commands', () => { expect([...find('f1').pieceKeys]).toEqual(['flooring:F1']); expect([...find('F1 + F12').pieceKeys]).toEqual(['flooring:F1', 'flooring:F12']); expect([...find('B1').pieceKeys]).toEqual(['skirting:B1']); expect(find('floor:B1').pieceKeys.size).toBe(0); }); it('keeps identical display labels in different materials distinct', () => { const skirt = { ...skirting, pieces: [...skirting.pieces, skirtPiece('F1', 'S8', 'room-2')] }; const result = findBoardMatches(project, flooring, skirt, 'F1'); expect([...result.pieceKeys]).toEqual(['flooring:F1', 'skirting:F1']); expect(result.materials).toEqual(new Set(['flooring', 'skirting'])); const data = cutData(flooring, skirt, project.flooring); expect(data.selections.get('flooring:F1')?.piece.roomId).toBe('room-1'); expect(data.selections.get('skirting:F1')?.piece.roomId).toBe('room-2'); }); it('locates all pieces of a source board across rooms without setting search text', () => { const focus = { kind: 'board', material: 'flooring', id: 'B1' } as const; const result = findBoardMatches(project, flooring, skirting, '', focus); expect([...result.pieceKeys]).toEqual(['flooring:F1', 'flooring:F12']); expect(result.roomIds).toEqual(['room-1', 'room-2']); expect(result.boardKeys).toEqual(new Set(['floor:B1'])); expect(focusLabel(focus, cuts)).toBe('F1 + F12'); }); it('locates uncut boards and searches room names including code-like names', () => { const focus = { kind: 'board', material: 'flooring', id: 'B3' } as const; expect([...findBoardMatches(project, flooring, skirting, '', focus).pieceKeys]).toEqual([ 'flooring:FU1', ]); expect(focusLabel(focus, cuts)).toBe('Uncut floorboard'); expect(find(' OfFiCe ').roomIds).toEqual(['room-2']); const renamed = { ...project, rooms: project.rooms.map((room) => ({ ...room, name: 'A101' })) }; expect(findBoardMatches(renamed, flooring, skirting, 'A101').pieceKeys.size).toBe(8); }); it('keeps a focused piece distinct from a focused source board', () => { expect([ ...findBoardMatches(project, flooring, skirting, '', { kind: 'piece', material: 'flooring', id: 'F12', }).pieceKeys, ]).toEqual(['flooring:F12']); }); it('returns no matches for empty or unknown input and does not mutate plans', () => { const before = structuredClone({ project, flooring, skirting }); expect(find(' ').pieceKeys.size).toBe(0); expect(find('missing').pieceKeys.size).toBe(0); expect({ project, flooring, skirting }).toEqual(before); }); });