Simplify
Some checks failed
Deploy to Pages / build (push) Failing after 10m45s

This commit is contained in:
Andras Schmelczer 2026-09-11 23:27:29 +01:00
commit 8bf3e3b705
40 changed files with 1443 additions and 1497 deletions

View file

@ -1,9 +1,7 @@
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { describe, expect, it } from 'vitest';
import { defaultProject, rectangle } from '../src/lib/defaults';
import { boardSearchLabel, findBoardMatches } from '../src/lib/board-tracking';
import { CutList } from '../src/components/CutList';
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();
@ -20,7 +18,6 @@ function floorPiece(id: string, stockId: string, roomId = 'room-1', cut = true):
blankLength: length,
blankWidth: 190,
row: 1,
column: 1,
polygon: [],
template: [
{ x: 0, y: 0 },
@ -69,6 +66,7 @@ function skirtPiece(id: string, stockId: string, roomId = 'room-1', length = 900
roomId,
length,
blankLength: length,
cut: length !== 2400,
backLength: length,
wallId: 'w1',
wallIndex: 0,
@ -81,10 +79,6 @@ function skirtPiece(id: string, stockId: string, roomId = 'room-1', length = 900
template,
installedTemplate: template,
visibleLength: length,
leftOverlap: 0,
rightOverlap: 0,
leftBevel: 0,
rightBevel: 0,
};
}
const skirting: SkirtingPlan = {
@ -104,122 +98,57 @@ const skirting: SkirtingPlan = {
waste: 1503,
issues: [],
};
const find = (query: string) => findBoardMatches(project, flooring, skirting, query);
const noop = () => {};
function list(search = '', completedBoards: string[] = []) {
return renderToStaticMarkup(
createElement(CutList, {
project,
flooring,
skirting,
select: noop,
search,
setSearch: noop,
completedBoards,
toggleBoard: noop,
locateBoard: noop,
locatePiece: noop,
}),
);
}
const cuts = cutData(flooring, skirting, project.flooring);
describe('board tracking search', () => {
it('matches cut and stock codes exactly instead of confusing F1 with F10/F12 or B1 with B10', () => {
expect(find(' F1 ')).toEqual({
pieceIds: ['F1'],
roomIds: ['room-1'],
boardKeys: ['floor:B1'],
});
expect(find('b1')).toEqual({
pieceIds: ['F1', 'F12', 'B1'],
roomIds: ['room-1', 'room-2'],
boardKeys: ['floor:B1', 'skirting:S6'],
});
expect(find('S6').pieceIds).toEqual(['A2', 'B1']);
expect(find('B10').pieceIds).toEqual(['F10', 'B10']);
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('uses material-qualified source keys for unambiguous board location and includes all its rooms', () => {
expect(find(' FLOOR:B1 ')).toEqual({
pieceIds: ['F1', 'F12'],
roomIds: ['room-1', 'room-2'],
boardKeys: ['floor:B1'],
});
expect(find('skirting:S6')).toEqual({
pieceIds: ['A2', 'B1'],
roomIds: ['room-1', 'room-2'],
boardKeys: ['skirting:S6'],
});
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('finds full uncut boards as well as cuts, and searches room names without case sensitivity', () => {
expect(find('b3')).toEqual({ pieceIds: ['FU1'], roomIds: ['room-2'], boardKeys: ['floor:B3'] });
expect(find('s7')).toEqual({
pieceIds: ['SU1'],
roomIds: ['room-2'],
boardKeys: ['skirting:S7'],
});
expect(find(' OfFiCe ')).toEqual({
pieceIds: ['F12', 'FU1', 'B1', 'SU1'],
roomIds: ['room-2'],
boardKeys: ['floor:B1', 'floor:B3', 'skirting:S6', 'skirting:S7'],
});
expect(find('living').roomIds).toEqual(['room-1']);
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('returns empty matches for empty or unknown queries and never mutates the plans', () => {
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(' ')).toEqual({ pieceIds: [], roomIds: [], boardKeys: [] });
expect(find('missing')).toEqual({ pieceIds: [], roomIds: [], boardKeys: [] });
expect(find('B1')).toEqual(find('B1'));
expect(find(' ').pieceKeys.size).toBe(0);
expect(find('missing').pieceKeys.size).toBe(0);
expect({ project, flooring, skirting }).toEqual(before);
});
it('keeps all cuts from a matching source together and applies completion to the whole group', () => {
const markup = list('F1', ['floor:B1']);
expect(markup).toContain('data-board-key="floor:B1"');
expect(markup).not.toContain('data-board-key="floor:B10"');
expect(markup).not.toContain('data-board-key="skirting:S6"');
expect(markup).toContain('Preview flooring cut F1');
expect(markup).toContain('Preview flooring cut F12');
expect(markup.match(/class="board-cut is-done/g)).toHaveLength(2);
expect(markup).toContain('aria-label="Mark cuts F1 + F12 done" checked=""');
expect(markup).toContain('Show cuts F1 + F12 on plan');
expect(markup).toContain('Show cut F12 on plan');
expect(markup).toContain('<span>Done</span>');
});
it('names board groups and actions using their cuts while keeping bookkeeping IDs internal', () => {
const markup = list();
expect(markup).toContain('aria-label="Flooring cuts F1 + F12"');
expect(markup).toContain('aria-label="Skirting cuts A2 + B1"');
expect(markup).toContain('Mark cuts A2 + B1 done');
expect(markup).toContain('data-board-key="skirting:S6"');
expect(markup).toContain('Find a cut or room');
expect(markup).not.toMatch(/(?:Floorboard B\d|Skirting board S\d|Export CSV)/i);
expect(markup).not.toMatch(/aria-label="[^"]*(?:floor:B\d|skirting:S\d)/i);
});
it('shows cut names when a located board is stored as a qualified source key', () => {
expect(boardSearchLabel(project, flooring, skirting, 'floor:B1')).toBe('F1 + F12');
expect(boardSearchLabel(project, flooring, skirting, 'skirting:S6')).toBe('A2 + B1');
expect(boardSearchLabel(project, flooring, skirting, 'Office')).toBe('Office');
expect(list('floor:B1')).toContain('value="F1 + F12"');
expect(list('skirting:S6')).toContain('value="A2 + B1"');
expect(find('F1 + F12')).toEqual(find('floor:B1'));
// B1 here is a skirting cut, not the hidden flooring stock with the same internal code.
expect(find('A2 + B1')).toEqual(find('skirting:S6'));
});
it('exposes an interactive full-board result only when searched, without inventing a cut preview', () => {
expect(list()).not.toContain('data-board-key="floor:B3"');
const markup = list('floor:B3');
expect(markup).toContain('data-board-key="floor:B3"');
expect(markup).toContain('Mark uncut floorboard done');
expect(markup).toContain('Show uncut floorboard on plan');
expect(markup).toContain('Installed uncut');
expect(markup).toContain('value="Uncut floorboard"');
expect(markup).not.toContain('Preview flooring cut FU1');
expect(list('skirting:S7')).toContain('value="Uncut skirting board"');
});
});