wip
This commit is contained in:
commit
d8f2407ddf
44 changed files with 10377 additions and 0 deletions
124
tests/cut-preview.test.ts
Normal file
124
tests/cut-preview.test.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { createElement } from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { FlooringPreview, SkirtingPreview } from '../src/components/CutPreview';
|
||||
import { exactMm, flooringTemplate, skirtingEndBoxes } from '../src/lib/cut-preview';
|
||||
import type { FlooringPiece, Point, SkirtingPiece } from '../src/lib/types';
|
||||
|
||||
const floor: FlooringPiece = {
|
||||
id: 'F1',
|
||||
roomId: 'room-1',
|
||||
row: 1,
|
||||
column: 1,
|
||||
polygon: [],
|
||||
length: 600.001,
|
||||
width: 190,
|
||||
stockId: 'B1',
|
||||
reused: false,
|
||||
cut: true,
|
||||
shaped: false,
|
||||
template: [],
|
||||
};
|
||||
const template: Point[] = [
|
||||
{ x: 18, y: 0 },
|
||||
{ x: 1182, y: 0 },
|
||||
{ x: 1200, y: 18 },
|
||||
{ x: 0, y: 18 },
|
||||
];
|
||||
const skirt: SkirtingPiece = {
|
||||
id: 'A1',
|
||||
roomId: 'room-1',
|
||||
wallId: 'w1',
|
||||
wallIndex: 0,
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 1164, y: 0 },
|
||||
length: 1200,
|
||||
backLength: 1164,
|
||||
leftMitre: 45,
|
||||
rightMitre: 45,
|
||||
leftType: 'external',
|
||||
rightType: 'external',
|
||||
stockId: 'S1',
|
||||
template,
|
||||
};
|
||||
|
||||
describe('cut shape previews', () => {
|
||||
it('draws rectangular floor cuts too, keeping their precise measured dimensions', () => {
|
||||
const points = flooringTemplate(floor);
|
||||
expect(points).toEqual([
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 600.001, y: 0 },
|
||||
{ x: 600.001, y: 190 },
|
||||
{ x: 0, y: 190 },
|
||||
]);
|
||||
const markup = renderToStaticMarkup(createElement(FlooringPreview, { piece: floor }));
|
||||
expect(markup).toContain('aria-label="Flooring shape F1"');
|
||||
expect(markup).toContain('preserveAspectRatio="xMidYMid meet"');
|
||||
expect(markup).toContain('Length 600.001 mm × width 190 mm');
|
||||
expect(exactMm(600.001)).toBe('600.001');
|
||||
});
|
||||
|
||||
it('keeps skirting endpoints in original coordinates and gives both ends equal square windows', () => {
|
||||
const original = structuredClone(template);
|
||||
const boxes = skirtingEndBoxes(template, 18);
|
||||
expect(boxes.left.width).toBe(boxes.left.height);
|
||||
expect(boxes.right.width).toBe(boxes.right.height);
|
||||
expect(boxes.left.width).toBe(boxes.right.width);
|
||||
// Equal x/y scales preserve the measured 18-by-18 mm, 45-degree cut.
|
||||
const scaleX = 120 / boxes.left.width,
|
||||
scaleY = 120 / boxes.left.height;
|
||||
const angle =
|
||||
(Math.atan2(
|
||||
Math.abs(template[3].y - template[0].y) * scaleY,
|
||||
Math.abs(template[3].x - template[0].x) * scaleX,
|
||||
) *
|
||||
180) /
|
||||
Math.PI;
|
||||
expect(angle).toBeCloseTo(45, 8);
|
||||
expect(template).toEqual(original);
|
||||
const markup = renderToStaticMarkup(
|
||||
createElement(SkirtingPreview, { piece: skirt, thickness: 18 }),
|
||||
);
|
||||
expect(markup).toContain('points="18,0 1182,0 1200,18 0,18"');
|
||||
expect(markup.match(/preserveAspectRatio="xMidYMid meet"/g)).toHaveLength(3);
|
||||
expect(markup).toContain('Full length 1,200 mm × thickness 18 mm');
|
||||
expect(markup).not.toContain('exaggerat');
|
||||
});
|
||||
|
||||
it('keeps the complete end cut visible even at shallow mitre angles', () => {
|
||||
const shallow = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 2000, y: 0 },
|
||||
{ x: 2100, y: 18 },
|
||||
{ x: 1000, y: 18 },
|
||||
];
|
||||
const boxes = skirtingEndBoxes(shallow, 18);
|
||||
for (const [name, indices] of [
|
||||
['left', [0, 3]],
|
||||
['right', [1, 2]],
|
||||
] as const) {
|
||||
const box = boxes[name];
|
||||
for (const index of indices) {
|
||||
expect(shallow[index].x).toBeGreaterThan(box.x);
|
||||
expect(shallow[index].x).toBeLessThan(box.x + box.width);
|
||||
expect(shallow[index].y).toBeGreaterThan(box.y);
|
||||
expect(shallow[index].y).toBeLessThan(box.y + box.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('makes thumbnails decorative while retaining true-scale SVGs and visible end breaks', () => {
|
||||
const floorMarkup = renderToStaticMarkup(
|
||||
createElement(FlooringPreview, { piece: floor, compact: true }),
|
||||
);
|
||||
const skirtMarkup = renderToStaticMarkup(
|
||||
createElement(SkirtingPreview, { piece: skirt, thickness: 18, compact: true }),
|
||||
);
|
||||
expect(floorMarkup).toContain('aria-hidden="true"');
|
||||
expect(skirtMarkup.match(/aria-hidden="true"/g)).toHaveLength(3);
|
||||
expect(skirtMarkup).toContain('>//</span>');
|
||||
expect(skirtMarkup.match(/preserveAspectRatio="xMidYMid meet"/g)).toHaveLength(2);
|
||||
expect(floorMarkup).not.toContain('<figure');
|
||||
expect(skirtMarkup).not.toContain('<figure');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue