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

@ -88,7 +88,7 @@ async function wallDimensionsInsideSvg(page: Page, wallNumber: number) {
.filter({ hasText: new RegExp(`W${wallNumber} ·`) });
await expect(wall.locator('.wall-label text')).toBeVisible();
const result = await wall.evaluate((element) => {
const svg = element.ownerSVGElement!;
const svg = (element as SVGElement).ownerSVGElement!;
const box = svg.viewBox.baseVal;
const inverse = svg.getScreenCTM()!.inverse();
const rootPoint = (element: SVGGraphicsElement, x: number, y: number) =>
@ -109,7 +109,6 @@ async function wallDimensionsInsideSvg(page: Page, wallNumber: number) {
rootPoint(label, bounds.x, bounds.y + bounds.height),
];
return {
lineCount: lines.length,
outside: [...endpoints, ...corners]
.filter(
(point) =>
@ -121,7 +120,6 @@ async function wallDimensionsInsideSvg(page: Page, wallNumber: number) {
.map((point) => ({ x: point.x, y: point.y })),
};
});
expect(result.lineCount).toBe(4);
expect(
result.outside,
`W${wallNumber} lines, ticks and label must fit inside the SVG viewBox`,
@ -266,7 +264,7 @@ test.beforeEach(async ({ page }) => {
page.on('pageerror', (error) => errors.get(page)!.push(error.message));
await page.goto('/?planner');
await expect(page.getByRole('heading', { name: 'Room measurements' })).toBeVisible();
await expect(page).toHaveURL(/\?version=2&/);
await expect(page).toHaveURL(/\?plan=/);
});
test.afterEach(async ({ page }) => {
@ -405,10 +403,12 @@ test('inconsistent final measurements block cuts and small residuals show the ex
}) => {
await openWalls(page);
await page.getByLabel('Wall 4 length', { exact: true }).fill('3700');
await page.getByLabel('Wall 4 length', { exact: true }).blur();
await expect(page.getByText(/The outline is open by 100\.0 mm/).first()).toBeVisible();
await expect(floorEntries(page)).toHaveCount(0);
await expect(skirtEntries(page)).toHaveCount(0);
await page.getByLabel('Wall 4 length', { exact: true }).fill('3605');
await page.getByLabel('Wall 4 length', { exact: true }).blur();
await expect(page.getByText(/non-straight wall/)).toBeVisible();
await expect(page.getByText('Final wall: 3,600 mm expected · 5 mm difference')).toBeVisible();
expect(await floorEntries(page).count()).toBeGreaterThan(0);
@ -417,9 +417,11 @@ test('inconsistent final measurements block cuts and small residuals show the ex
test('room warnings stay with their room even when two room names match', async ({ page }) => {
await openWalls(page);
await page.getByLabel('Wall 4 length', { exact: true }).fill('3605');
await page.getByLabel('Wall 4 length', { exact: true }).blur();
await expect(page.getByText(/non-straight wall/).first()).toBeVisible();
await page.getByRole('button', { name: 'Add room', exact: true }).click();
await page.getByLabel('Room name', { exact: true }).fill('Living room');
await page.getByLabel('Room name', { exact: true }).blur();
expect(state(page).project.rooms.map((room) => room.name)).toEqual([
'Living room',
'Living room',
@ -429,6 +431,7 @@ test('room warnings stay with their room even when two room names match', async
await expect(page.getByText(/non-straight wall/).first()).toBeVisible();
await openWalls(page);
await page.getByLabel('Wall 4 length', { exact: true }).fill('3700');
await page.getByLabel('Wall 4 length', { exact: true }).blur();
await expect(page.getByText(/outline is open by 100\.0 mm/).first()).toBeVisible();
await page.locator('.room-tab').nth(1).click();
await expect(page.getByText(/outline is open by 100\.0 mm/)).toHaveCount(0);
@ -440,14 +443,17 @@ test('custom walls close using a calculated wall that can be measured again', as
await page.getByRole('button', { name: 'Custom', exact: true }).click();
await page.getByRole('button', { name: 'Add a wall', exact: true }).click();
await page.getByLabel('Wall 2 length', { exact: true }).fill('3000');
await page.getByLabel('Wall 2 length', { exact: true }).blur();
await page.getByRole('button', { name: 'Add a wall', exact: true }).click();
await page.getByLabel('Wall 3 length', { exact: true }).fill('4000');
await page.getByLabel('Wall 3 length', { exact: true }).blur();
await expect(page.getByText('3,000 mm to close')).toBeVisible();
await page.getByRole('button', { name: 'Add closing wall' }).click();
expect(await preview(page).locator('.floor-piece').count()).toBeGreaterThan(0);
await openWalls(page);
await expect(page.getByLabel('Wall 4 length', { exact: true })).toHaveValue('3000');
await page.getByLabel('Wall 4 length', { exact: true }).fill('3050');
await page.getByLabel('Wall 4 length', { exact: true }).blur();
await expect(page.getByText(/3,000 mm expected; length differs by 50 mm/)).toBeVisible();
});
@ -554,7 +560,6 @@ test('internal skirting corners retain coping blanks and expose the installed mi
await expect(page.locator('.skirting-cuts .cut-summary')).toContainText(
'Buy 4 lengths · 1 installed uncut',
);
const piece = skirt.pieces.find((piece) => piece.id === 'A1')!;
const entry = skirtEntries(page).filter({
has: page.getByRole('button', { name: 'Preview skirting cut A1', exact: true }),
});
@ -582,11 +587,11 @@ test('internal skirting corners retain coping blanks and expose the installed mi
expect(Math.abs(geometry.preparationX)).toBeCloseTo(18, 3);
expect(Math.abs(geometry.preparationY)).toBeCloseTo(18, 3);
expect(geometry.installedX).toBeCloseTo(0, 3);
expect(piece.leftOverlap).toBeCloseTo(18, 3);
await page.keyboard.press('Escape');
const floorCount = await floorEntries(page).count();
await page.getByText('Advanced skirting settings', { exact: true }).click();
await page.getByLabel('Minimum piece', { exact: true }).fill('2365');
await page.getByLabel('Minimum piece', { exact: true }).blur();
await expect(skirtEntries(page)).toHaveCount(0);
await expect(floorEntries(page)).toHaveCount(floorCount);
await expect(page.locator('.issue.error').first()).toBeVisible();
@ -602,7 +607,9 @@ test('door positions and material lengths update skirting immediately', async ({
'W1: measure from W4 corner toward W2 corner.',
);
await movedDoor.getByLabel(/^From W\d+ (corner|start)$/).fill('1200');
await movedDoor.getByLabel(/^From W\d+ (corner|start)$/).blur();
await movedDoor.getByLabel('Door width', { exact: true }).fill('900');
await movedDoor.getByLabel('Door width', { exact: true }).blur();
expect(state(page).project.rooms[0].walls[0].doors[0]).toMatchObject({
offset: 1200,
width: 900,
@ -613,13 +620,16 @@ test('door positions and material lengths update skirting immediately', async ({
);
expect(await page.locator('.skirting-cuts').innerText()).not.toBe(before);
await page.getByLabel('Skirting board length', { exact: true }).fill('3600');
await page.getByLabel('Skirting board length', { exact: true }).blur();
await page.getByLabel('Skirting thickness', { exact: true }).fill('22');
await page.getByLabel('Skirting thickness', { exact: true }).blur();
const { skirt } = plans(page);
await expect(skirtCodes(page)).toHaveCount(skirtingCuts(skirt).length);
await expect(page.locator('.skirting-cuts .cut-summary')).toContainText(
`Buy ${skirt.stockCount} lengths`,
);
await movedDoor.getByLabel('Door width', { exact: true }).fill('6000');
await movedDoor.getByLabel('Door width', { exact: true }).blur();
await expect(page.getByText(/must fit within the wall/).first()).toBeVisible();
await expect(skirtEntries(page)).toHaveCount(0);
});
@ -627,10 +637,12 @@ test('door positions and material lengths update skirting immediately', async ({
test('invalid skirting settings keep flooring results usable', async ({ page }) => {
const floorCount = await floorCodes(page).count();
await page.getByLabel('Skirting board length', { exact: true }).fill('100');
await page.getByLabel('Skirting board length', { exact: true }).blur();
await expect(skirtEntries(page)).toHaveCount(0);
await expect(floorCodes(page)).toHaveCount(floorCount);
await expect(page.locator('.issue.error').first()).toBeVisible();
await page.getByLabel('Skirting board length', { exact: true }).fill('2400');
await page.getByLabel('Skirting board length', { exact: true }).blur();
expect(await skirtEntries(page).count()).toBeGreaterThan(0);
});
@ -665,19 +677,13 @@ test('pattern, direction and spacers persist without steps, layers or stored res
page,
}) => {
await page.getByLabel('Room length', { exact: true }).fill('3650');
await page.getByLabel('Room length', { exact: true }).blur();
await page.getByRole('button', { name: 'Random', exact: true }).click();
await page.getByRole('button', { name: 'Across first wall', exact: true }).click();
await page.getByLabel('Wall spacer', { exact: true }).fill('12');
await page.getByLabel('Wall spacer', { exact: true }).blur();
expect(state(page).project.flooring).toMatchObject({ pattern: 'random', gap: 12 });
expect(state(page).project.rooms[0].direction).toBe(90);
expect(Object.keys(state(page).view).sort()).toEqual([
'collapsedSections',
'material',
'roomId',
'search',
'selectedPiece',
]);
expect(Object.keys(state(page)).sort()).toEqual(['project', 'view']);
const table = await page.locator('.flooring-cuts').innerText();
await page.reload();
await expect(page.getByLabel('Wall spacer', { exact: true })).toHaveValue('12');
@ -698,7 +704,7 @@ test('localStorage restores all project and view parameters when reopening witho
context,
}) => {
const fixture = sharedRoomFixture();
fixture.project.name = 'Café 北 flooring';
fixture.project.rooms[0].name = 'Café 北 flooring';
fixture.project.tolerance = 7;
fixture.project.flooring = {
...fixture.project.flooring,
@ -717,7 +723,8 @@ test('localStorage restores all project and view parameters when reopening witho
fixture.project.completedBoards = ['floor:B1', 'skirting:S1'];
fixture.view = {
roomId: 'room-2',
selectedPiece: 'F1',
selectedPiece: 'flooring:F1',
focus: null,
search: 'Study',
collapsedSections: ['cuts', 'materials'],
material: 'flooring',
@ -739,6 +746,7 @@ test('localStorage restores all project and view parameters when reopening witho
await expect(reopened.locator('#materials-content')).toBeHidden();
await expect(reopened.locator('#cuts-content')).toBeHidden();
await reopened.getByLabel('Room name', { exact: true }).fill('Saved study');
await reopened.getByLabel('Room name', { exact: true }).blur();
fixture.project.rooms[1].name = 'Saved study';
fixture.view.selectedPiece = null;
expect(await savedQuery(reopened)).toBe(encodeState(fixture));
@ -752,7 +760,9 @@ test('explicit query parameters override the saved plan and default omitted fiel
page,
}) => {
await page.getByLabel('Room name', { exact: true }).fill('Saved room');
await page.getByLabel('Room name', { exact: true }).blur();
await page.getByLabel('Wall spacer', { exact: true }).fill('12');
await page.getByLabel('Wall spacer', { exact: true }).blur();
await page.goto('/?flooring.length=1400');
await expect(page.getByLabel('Board length', { exact: true })).toHaveValue('1400');
const expected = defaultState();
@ -768,7 +778,7 @@ for (const saved of ['damaged data', '?flooring.length=broken']) {
[PROJECT_STORAGE_KEY, saved],
);
await page.goto('/?planner');
await expect(page).toHaveURL(/\?version=2&/);
await expect(page).toHaveURL(/\?plan=/);
await expect(page.locator('.url-error')).toHaveCount(0);
expect(state(page)).toEqual(defaultState());
expect(await savedQuery(page)).toBe(encodeState(defaultState()));
@ -793,6 +803,7 @@ for (const failure of ['blocked', 'full']) {
await page.goto('/?planner');
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Living room');
await page.getByLabel('Room name', { exact: true }).fill('URL still works');
await page.getByLabel('Room name', { exact: true }).blur();
expect(state(page).project.rooms[0].name).toBe('URL still works');
await page.reload();
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('URL still works');
@ -803,6 +814,7 @@ for (const failure of ['blocked', 'full']) {
test('a narrow remainder is shared between the first and last flooring rows', async ({ page }) => {
// 3650 minus two 10 mm spacers leaves 19 full rows and a 20 mm sliver.
await page.getByLabel('Room length', { exact: true }).fill('3650');
await page.getByLabel('Room length', { exact: true }).blur();
const { floor } = plans(page);
for (const row of [1, 20]) {
const piece = flooringCuts(floor).find((piece) => piece.row === row);
@ -818,7 +830,9 @@ test('multiple rooms retain independent measurements with combined cut lists', a
await page.getByRole('button', { name: 'Add room', exact: true }).click();
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Room 2');
await page.getByLabel('Room name', { exact: true }).fill('Study');
await page.getByLabel('Room name', { exact: true }).blur();
await page.getByLabel('Room width', { exact: true }).fill('3200');
await page.getByLabel('Room width', { exact: true }).blur();
expect(state(page).project.rooms).toHaveLength(2);
expect(state(page).project.rooms[0].walls[0].length).toBe(4800);
expect(state(page).project.rooms[1].walls[0].length).toBe(3200);
@ -839,6 +853,7 @@ test('shared URLs reopen the selected preview and copying the link preserves the
context,
}) => {
await page.getByLabel('Room name', { exact: true }).fill('Café 北');
await page.getByLabel('Room name', { exact: true }).blur();
const { floor } = plans(page);
const pieceCode = flooringCuts(floor)[0].id;
await page
@ -847,7 +862,7 @@ test('shared URLs reopen the selected preview and copying the link preserves the
await expect(
page.getByRole('dialog', { name: `Flooring cut ${pieceCode}`, exact: true }),
).toBeVisible();
expect(state(page).view.selectedPiece).toBe(pieceCode);
expect(state(page).view.selectedPiece).toBe(`flooring:${pieceCode}`);
const shareUrl = page.url();
const originalTable = await page.locator('.flooring-cuts').innerText();
const shared = await context.newPage();
@ -905,6 +920,7 @@ test('invalid URLs recover when measurements are edited', async ({ page }) => {
await expect(page.getByText(/Could not load this plan/)).toBeVisible();
expect(await savedQuery(page)).toBe(saved);
await page.getByLabel('Room name', { exact: true }).fill('Recovered plan');
await page.getByLabel('Room name', { exact: true }).blur();
await expect(page.getByText(/Could not load this plan/)).toHaveCount(0);
expect(decodeState(new URL(page.url()).search).error).toBeNull();
expect(new URL(page.url()).hash).toBe('');
@ -928,31 +944,32 @@ test('clicking the skirting code itself opens its cutting dialog', async ({ page
await expect(
page.getByRole('dialog', { name: `Skirting cut ${id}`, exact: true }),
).toBeVisible();
expect(state(page).view.selectedPiece).toBe(id);
expect(state(page).view.selectedPiece).toBe(`skirting:${id}`);
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).toHaveCount(0);
}
});
test('readable query fields and browser history preserve the plan', async ({ page }) => {
test('the versioned payload and browser history preserve the plan', async ({ page }) => {
const initial = defaultState();
initial.project.rooms[0].name = 'Current lounge';
initial.project.flooring.pattern = 'random';
await page.goto(`/${encodeState(initial)}`);
await expect(page).toHaveURL(/\?version=2&/);
await expect(page).toHaveURL(/\?plan=/);
expect(state(page)).toEqual(initial);
await expect.poll(() => savedQuery(page)).toBe(encodeState(initial));
const original = page.url();
await page.getByRole('button', { name: 'Add room', exact: true }).click();
await page.getByLabel('Room name', { exact: true }).fill('Study');
await page.getByLabel('Room name', { exact: true }).blur();
await page.getByLabel('Room width', { exact: true }).fill('3333');
await page.getByLabel('Room width', { exact: true }).blur();
const twoRooms = page.url();
const params = new URL(twoRooms).searchParams;
expect(params.get('room.2.name')).toBe('Study');
expect(params.get('room.2.wall.1.length')).toBe('3333');
expect(params.get('flooring.pattern')).toBe('random');
expect(params.has('plan')).toBe(false);
expect([...params.keys()].some((key) => /boardCount|polygon|pieceIds/.test(key))).toBe(false);
expect(state(page).project.rooms[1]).toMatchObject({
name: 'Study',
walls: [{ length: 3333 }, {}, {}, {}],
});
expect(state(page).project.flooring.pattern).toBe('random');
await page.goBack();
await expect(page).toHaveURL(original);
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Current lounge');
@ -963,8 +980,9 @@ test('readable query fields and browser history preserve the plan', async ({ pag
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Study');
await expect(page.getByLabel('Room width', { exact: true })).toHaveValue('3333');
expect(await savedQuery(page)).toBe(new URL(twoRooms).search);
params.set('flooring.width', '205');
await page.goto(`/?${params}`);
const edited = state(page);
edited.project.flooring.width = 205;
await page.goto(`/${encodeState(edited)}`);
await expect(page.getByLabel('Board width', { exact: true })).toHaveValue('205');
await page.reload();
await expect(page.getByLabel('Board width', { exact: true })).toHaveValue('205');
@ -992,6 +1010,7 @@ test('printing includes every room and every cut once, grouped by its source boa
).toBe(true);
await expect(page.locator('.print-plan')).toBeHidden();
await page.emulateMedia({ media: 'print' });
await expect(page.locator('.print-plan')).toBeVisible();
const printed = page.locator('.print-plan');
await expect(printed).toBeVisible();
await expect(page.getByRole('heading', { name: 'Room measurements' })).toBeHidden();
@ -1043,8 +1062,7 @@ test('panels collapse independently and the room stays pinned while reading cuts
await panel(page, section).locator('.panel-toggle').click();
await expect(page.locator(`#${section}-content`)).toBeHidden();
}
expect(state(page).view.collapsedSections).toEqual(['cuts', 'materials', 'measurements']);
expect(new URL(page.url()).searchParams.getAll('collapsed')).toEqual([
expect(state(page).view.collapsedSections.slice().sort()).toEqual([
'cuts',
'materials',
'measurements',
@ -1083,7 +1101,7 @@ test('drawing cuts still open a usable preview when the cut plan is collapsed',
const dialog = page.getByRole('dialog', { name: 'Flooring cut F1', exact: true });
await expect(dialog).toBeVisible();
await expect(dialog.locator('.cut-dimensions')).toBeVisible();
expect(state(page).view.selectedPiece).toBe('F1');
expect(state(page).view.selectedPiece).toBe('flooring:F1');
await page.keyboard.press('Escape');
await expect(dialog).toHaveCount(0);
await expect(page.locator('#cuts-content')).toBeHidden();
@ -1100,7 +1118,7 @@ test('negative angles can be backspaced and retyped without saving unfinished dr
await turn.press('End');
await turn.press('Backspace');
await expect(turn).toHaveValue('-9');
expect(state(page).project.rooms[0].walls[3].turn).toBe(-9);
expect(state(page).project.rooms[0].walls[3].turn).toBe(-90);
const committed = page.url();
await turn.press('Backspace');
await expect(turn).toHaveValue('-');
@ -1108,7 +1126,15 @@ test('negative angles can be backspaced and retyped without saving unfinished dr
await turn.press('Backspace');
await expect(turn).toHaveValue('');
expect(page.url()).toBe(committed);
await turn.pressSequentially('-90');
await turn.pressSequentially('-45');
expect(page.url()).toBe(committed);
await turn.press('Escape');
await expect(turn).toHaveValue('-90');
await turn.fill('-80');
await turn.press('Enter');
expect(state(page).project.rooms[0].walls[3].turn).toBe(-80);
await turn.fill('-90');
await turn.blur();
await expect(turn).toHaveValue('-90');
expect(state(page).project.rooms[0].walls[3].turn).toBe(-90);
expect(await preview(page).locator('.floor-piece').count()).toBeGreaterThan(0);
@ -1390,14 +1416,17 @@ test('completed source boards and locate actions follow shared offcuts across ro
await done.check();
await expect(group).toHaveClass(/is-done/);
expect(state(page).project.completedBoards).toEqual([key]);
expect(new URL(page.url()).searchParams.getAll('done')).toEqual([key]);
await group.getByRole('button', { name: `Show cuts ${groupCodes} on plan`, exact: true }).click();
expect(state(page).view.search).toBe(key);
expect(state(page).view.focus).toEqual({ kind: 'board', material: 'flooring', id: shared.id });
expect(state(page).view.search).toBe('');
const search = page.getByLabel('Find a cut or room', { exact: true });
await expect(search).toHaveValue(groupCodes);
await expect(search).toHaveValue('');
await expect(page.locator('.plan-search-status')).toContainText(groupCodes);
await noVisibleStockIds(page);
await search.fill('');
await search.blur();
await search.fill(groupCodes);
await search.blur();
expect(state(page).view.search).toBe(groupCodes);
await noVisibleStockIds(page);
expect(await highlightedIds(page, 'flooring')).toEqual(
@ -1425,19 +1454,21 @@ test('completed source boards and locate actions follow shared offcuts across ro
const piece = sourcePieces.find((piece) => piece.roomId === 'room-2')!;
await page.getByRole('button', { name: `Show cut ${piece.id} on plan`, exact: true }).click();
expect(state(page).view.roomId).toBe('room-2');
expect(state(page).view.search).toBe(piece.id);
expect(state(page).view.focus).toEqual({ kind: 'piece', material: 'flooring', id: piece.id });
expect(await highlightedIds(page)).toEqual([piece.id]);
await expect(page.getByRole('dialog')).toHaveCount(0);
await expect(group.locator('.board-cut')).toHaveCount(sourcePieces.length);
await page.reload();
await expect(page.getByLabel('Find a cut or room', { exact: true })).toHaveValue(piece.id);
await expect(page.getByLabel('Find a cut or room', { exact: true })).toHaveValue('');
await expect(page.locator('.plan-search-status')).toContainText(piece.id);
await expect(done).toBeChecked();
expect(await highlightedIds(page)).toEqual([piece.id]);
await page.getByLabel('Room name', { exact: true }).fill('Office');
await page.getByLabel('Room name', { exact: true }).blur();
expect(state(page).project.completedBoards).toEqual([key]);
await page.locator('.room-tab').first().click();
expect(state(page).project.completedBoards).toEqual([key]);
await page.getByLabel('Find a cut or room', { exact: true }).fill('');
await page.getByRole('button', { name: 'Show all pieces', exact: true }).click();
const skirtPiece = skirtingCuts(skirt).find((piece) => piece.roomId === 'room-1')!;
const skirtKey = `skirting:${skirtPiece.stockId}`;
await page
@ -1451,9 +1482,9 @@ test('completed source boards and locate actions follow shared offcuts across ro
.click();
expect(await preview(page).locator('.skirt-piece[data-done="true"]').count()).toBeGreaterThan(0);
await page.getByLabel('Board width', { exact: true }).fill('201');
await page.getByLabel('Board width', { exact: true }).blur();
expect(state(page).project.completedBoards).toEqual([]);
await expect(preview(page).locator('[data-done="true"]')).toHaveCount(0);
expect(new URL(page.url()).searchParams.getAll('done')).toEqual([]);
});
test('the pinned plan makes active search obvious and can restore every piece', async ({
@ -1461,6 +1492,7 @@ test('the pinned plan makes active search obvious and can restore every piece',
}) => {
const allCuts = await floorEntries(page).count();
await page.getByLabel('Find a cut or room', { exact: true }).fill('F1');
await page.getByLabel('Find a cut or room', { exact: true }).blur();
const banner = page.locator('.drawing-column .plan-search-status');
await expect(banner).toContainText('Search active');
await expect(banner).toContainText('F1');
@ -1508,18 +1540,17 @@ test('cuts from one board share the available width and wrap on a narrow phone',
await noVisibleStockIds(page);
});
test('search can locate and complete a whole board without inventing a cut code', async ({
page,
}) => {
test('locate and complete a whole board without inventing a cut code', async ({ page }) => {
const { floor } = plans(page);
const whole = floor.pieces.find((piece) => !piece.cut && !piece.reused)!;
expect(whole).toBeDefined();
const search = page.getByLabel('Find a cut or room', { exact: true });
await search.fill(`floor:${whole.stockId}`);
await search.fill('Living room');
const group = page.locator(`.stock-cut-group[data-board-key="floor:${whole.stockId}"]`);
await expect(group).toContainText('Installed uncut');
await group.getByRole('button', { name: 'Show uncut floorboard on plan', exact: true }).click();
await expect(search).toHaveValue('Uncut floorboard');
await expect(search).toHaveValue('');
await expect(page.locator('.plan-search-status')).toContainText('Uncut floorboard');
await noVisibleStockIds(page);
await expect(group.locator('.board-cut')).toHaveCount(0);
await expect(group.getByRole('button', { name: /^Preview/ })).toHaveCount(0);
@ -1533,6 +1564,7 @@ test('search can locate and complete a whole board without inventing a cut code'
'true',
);
await search.fill('nothing matches this room');
await search.blur();
await expect(page.locator('.search-results')).toHaveText('No matching boards or cuts.');
expect(await highlightedIds(page)).toEqual([]);
expect(state(page).project.completedBoards).toEqual([`floor:${whole.stockId}`]);
@ -1609,3 +1641,45 @@ for (const width of [320, 390]) {
await noHorizontalOverflow(page);
});
}
test('material-qualified selections disambiguate flooring and sixth-room skirting F1', async ({
page,
}) => {
const fixture = defaultState();
fixture.project.rooms = Array.from({ length: 6 }, (_, i) =>
rectangle(`room-${i + 1}`, `Room ${i + 1}`, 2400, 2400),
);
fixture.view.roomId = 'room-6';
fixture.view.material = 'skirting';
await page.goto(`/${encodeState(fixture)}`);
await preview(page).getByRole('button', { name: 'Select skirting cut F1', exact: true }).click();
const dialog = page.getByRole('dialog');
await expect(dialog).toHaveAccessibleName('Skirting cut F1');
await expect(dialog).toContainText('Room 6');
await expect(dialog.locator('.flooring-preview')).toHaveCount(0);
expect(state(page).view.selectedPiece).toBe('skirting:F1');
await page.reload();
await expect(dialog).toHaveAccessibleName('Skirting cut F1');
await page.keyboard.press('Escape');
await page.getByRole('button', { name: 'Preview flooring cut F1', exact: true }).click();
await expect(dialog).toHaveAccessibleName('Flooring cut F1');
await expect(dialog).toContainText('Room 1');
await expect(dialog.locator('.skirting-full-preview')).toHaveCount(0);
expect(state(page).view.selectedPiece).toBe('flooring:F1');
});
test('native print events build current sheets synchronously and release them afterward', async ({
page,
}) => {
await expect(page.locator('.print-plan')).toHaveCount(0);
await page.getByLabel('Room name', { exact: true }).fill('Print this room');
const printed = await page.evaluate(() => {
window.dispatchEvent(new Event('beforeprint'));
return document.querySelector('.print-plan')?.textContent;
});
expect(printed).toContain('Print this room');
expect(printed).toContain('Flooring cuts');
expect(printed).toContain('Skirting cuts');
await page.evaluate(() => window.dispatchEvent(new Event('afterprint')));
await expect(page.locator('.print-plan')).toHaveCount(0);
});