better
This commit is contained in:
parent
b9485e4097
commit
0c0f08b350
45 changed files with 1565 additions and 1579 deletions
|
|
@ -5,14 +5,19 @@ test('the home page explains the app and its guides fit the screen', async ({ pa
|
|||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
await page.goto('/');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toHaveText(
|
||||
'A great floorstarts witha little planning.',
|
||||
'A great floor starts with a little planning.',
|
||||
);
|
||||
await expect(page.getByRole('heading', { name: 'Scotia or skirting board?' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Plan my floor' })).toHaveCount(2);
|
||||
await expect(page.getByRole('link', { name: 'Plan my floor' })).toHaveCount(1);
|
||||
await expect(page.getByRole('link', { name: 'Plan my floor' }).first()).toBeInViewport();
|
||||
await page.getByRole('link', { name: 'First time fitting a floor?' }).click();
|
||||
await expect(page).toHaveURL(/#guide$/);
|
||||
await expect(page.getByRole('heading', { name: /Your first floor/ })).toBeInViewport();
|
||||
await expect(page.getByRole('link', { name: 'Open planner' })).toBeInViewport();
|
||||
const header = await page.getByRole('banner').boundingBox();
|
||||
const guide = await page.getByRole('heading', { name: /Your first floor/ }).boundingBox();
|
||||
expect(header!.y).toBe(0);
|
||||
expect(guide!.y).toBeGreaterThanOrEqual(header!.height);
|
||||
await page.reload();
|
||||
await expect(page.getByRole('heading', { name: /Your first floor/ })).toBeInViewport();
|
||||
await page.getByRole('link', { name: 'Back to top' }).click();
|
||||
|
|
@ -26,16 +31,19 @@ test('the home page explains the app and its guides fit the screen', async ({ pa
|
|||
for (const [name, selector] of [
|
||||
['header', '.landing-header'],
|
||||
['hero', '.landing-hero'],
|
||||
['closing', '.landing-final-cta'],
|
||||
]) {
|
||||
test(`the ${name} CTA opens a working planner and browser history returns home`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
if (name === 'header') {
|
||||
await page.getByRole('contentinfo').scrollIntoViewIfNeeded();
|
||||
await expect(page.getByRole('link', { name: 'Open planner' })).toBeInViewport();
|
||||
}
|
||||
await page.locator(selector).locator('a[href="?planner"]').click();
|
||||
await expect(page.getByRole('heading', { name: 'Room measurements' })).toBeVisible();
|
||||
await expect(page.getByLabel('Room width', { exact: true })).toHaveValue('4800');
|
||||
await expect(page).toHaveURL(/\?version=1&/);
|
||||
await expect(page).toHaveURL(/\?version=\d+&/);
|
||||
await page.getByLabel('Room name', { exact: true }).fill('My first floor');
|
||||
const projectUrl = page.url();
|
||||
await page.reload();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import LZString from 'lz-string';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { decodeState, encodeState } from '../src/lib/url-state';
|
||||
import { PROJECT_STORAGE_KEY } from '../src/lib/use-project';
|
||||
|
|
@ -9,6 +8,7 @@ import { planFlooring } from '../src/lib/flooring';
|
|||
import { planSkirting } from '../src/lib/skirting';
|
||||
import { flooringCuts, skirtingCuts, uncutFlooring } from '../src/lib/cut-list';
|
||||
import { cutMm } from '../src/lib/format';
|
||||
import type { UrlState } from '../src/lib/types';
|
||||
|
||||
const errors = new WeakMap<Page, string[]>();
|
||||
const panel = (page: Page, section: string) =>
|
||||
|
|
@ -22,7 +22,7 @@ const skirtCodes = (page: Page) =>
|
|||
skirtEntries(page).getByRole('button', { name: /^Preview skirting cut / });
|
||||
const state = (page: Page) => {
|
||||
const url = new URL(page.url());
|
||||
return decodeState(url.search || url.hash).state;
|
||||
return decodeState(url.search).state;
|
||||
};
|
||||
const savedQuery = (page: Page) =>
|
||||
page.evaluate((key) => localStorage.getItem(key), PROJECT_STORAGE_KEY);
|
||||
|
|
@ -60,6 +60,7 @@ function shortFireplaceFixture() {
|
|||
length: 1200,
|
||||
width: 190,
|
||||
pattern: 'random',
|
||||
optimisation: 'cuts',
|
||||
minOffset: 100,
|
||||
maxOffset: 1000,
|
||||
minLength: 10,
|
||||
|
|
@ -265,7 +266,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=1&/);
|
||||
await expect(page).toHaveURL(/\?version=2&/);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
|
|
@ -345,10 +346,14 @@ test('source-board groups contain every numbered cut once and leave full boards
|
|||
}
|
||||
await expect(page.locator('.material-cuts table')).toHaveCount(0);
|
||||
await expect(preview(page).locator('.floor-piece')).toHaveCount(floor.pieces.length);
|
||||
await expect(preview(page).locator('.skirt-piece')).toHaveCount(skirt.pieces.length);
|
||||
await expect(preview(page).getByRole('button', { name: /^Select flooring cut / })).toHaveCount(
|
||||
cutFloor.length,
|
||||
);
|
||||
await page
|
||||
.getByRole('group', { name: 'Plan view' })
|
||||
.getByRole('button', { name: 'Skirting', exact: true })
|
||||
.click();
|
||||
await expect(preview(page).locator('.skirt-piece')).toHaveCount(skirt.pieces.length);
|
||||
await expect(preview(page).getByRole('button', { name: /^Select skirting cut / })).toHaveCount(
|
||||
cutSkirt.length,
|
||||
);
|
||||
|
|
@ -539,7 +544,7 @@ test('internal skirting corners retain coping blanks and expose the installed mi
|
|||
expect(
|
||||
skirt.pieces.every(
|
||||
(piece) =>
|
||||
Math.abs(piece.length - 2400) < 0.001 && Math.abs(piece.visibleLength! - 2364) < 0.001,
|
||||
Math.abs(piece.length - 2400) < 0.001 && Math.abs(piece.visibleLength - 2364) < 0.001,
|
||||
),
|
||||
).toBe(true);
|
||||
const receiver = skirt.pieces.find((piece) => piece.wallIndex === 0)!;
|
||||
|
|
@ -580,6 +585,7 @@ test('internal skirting corners retain coping blanks and expose the installed mi
|
|||
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 expect(skirtEntries(page)).toHaveCount(0);
|
||||
await expect(floorEntries(page)).toHaveCount(floorCount);
|
||||
|
|
@ -666,6 +672,7 @@ test('pattern, direction and spacers persist without steps, layers or stored res
|
|||
expect(state(page).project.rooms[0].direction).toBe(90);
|
||||
expect(Object.keys(state(page).view).sort()).toEqual([
|
||||
'collapsedSections',
|
||||
'material',
|
||||
'roomId',
|
||||
'search',
|
||||
'selectedPiece',
|
||||
|
|
@ -713,6 +720,7 @@ test('localStorage restores all project and view parameters when reopening witho
|
|||
selectedPiece: 'F1',
|
||||
search: 'Study',
|
||||
collapsedSections: ['cuts', 'materials'],
|
||||
material: 'flooring',
|
||||
};
|
||||
const query = encodeState(fixture);
|
||||
await page.goto(`/${query}`);
|
||||
|
|
@ -760,7 +768,7 @@ for (const saved of ['damaged data', '?flooring.length=broken']) {
|
|||
[PROJECT_STORAGE_KEY, saved],
|
||||
);
|
||||
await page.goto('/?planner');
|
||||
await expect(page).toHaveURL(/\?version=1&/);
|
||||
await expect(page).toHaveURL(/\?version=2&/);
|
||||
await expect(page.locator('.url-error')).toHaveCount(0);
|
||||
expect(state(page)).toEqual(defaultState());
|
||||
expect(await savedQuery(page)).toBe(encodeState(defaultState()));
|
||||
|
|
@ -869,6 +877,8 @@ test('shared URLs reopen the selected preview and copying the link preserves the
|
|||
});
|
||||
|
||||
test('saw kerf help works with keyboard and touch or mouse', async ({ page, isMobile }) => {
|
||||
await page.getByText('Advanced flooring settings', { exact: true }).click();
|
||||
await page.getByText('Advanced skirting settings', { exact: true }).click();
|
||||
for (const name of ['What is saw kerf?', 'What is skirting saw kerf?']) {
|
||||
const button = page.getByRole('button', { name, exact: true });
|
||||
await button.focus();
|
||||
|
|
@ -904,6 +914,10 @@ test('invalid URLs recover when measurements are edited', async ({ page }) => {
|
|||
});
|
||||
|
||||
test('clicking the skirting code itself opens its cutting dialog', async ({ page }) => {
|
||||
await page
|
||||
.getByRole('group', { name: 'Plan view' })
|
||||
.getByRole('button', { name: 'Skirting', exact: true })
|
||||
.click();
|
||||
for (const target of ['rect', 'text']) {
|
||||
const code = preview(page).locator('.skirting-code').first();
|
||||
const id = await code.evaluate((element) =>
|
||||
|
|
@ -920,18 +934,14 @@ test('clicking the skirting code itself opens its cutting dialog', async ({ page
|
|||
}
|
||||
});
|
||||
|
||||
test('readable query fields, legacy links and browser history preserve the plan', async ({
|
||||
page,
|
||||
}) => {
|
||||
const legacy = defaultState();
|
||||
legacy.project.rooms[0].name = 'Legacy lounge';
|
||||
legacy.project.flooring.pattern = 'random';
|
||||
const hash = `#plan=${LZString.compressToEncodedURIComponent(JSON.stringify(legacy))}`;
|
||||
await page.goto(`/${hash}`);
|
||||
await expect(page).toHaveURL(/\?version=1&/);
|
||||
expect(new URL(page.url()).hash).toBe('');
|
||||
expect(state(page)).toEqual(legacy);
|
||||
expect(await savedQuery(page)).toBe(encodeState(legacy));
|
||||
test('readable query fields 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&/);
|
||||
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');
|
||||
|
|
@ -945,7 +955,7 @@ test('readable query fields, legacy links and browser history preserve the plan'
|
|||
expect([...params.keys()].some((key) => /boardCount|polygon|pieceIds/.test(key))).toBe(false);
|
||||
await page.goBack();
|
||||
await expect(page).toHaveURL(original);
|
||||
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Legacy lounge');
|
||||
await expect(page.getByLabel('Room name', { exact: true })).toHaveValue('Current lounge');
|
||||
expect(state(page).project.rooms).toHaveLength(1);
|
||||
expect(await savedQuery(page)).toBe(new URL(original).search);
|
||||
await page.goForward();
|
||||
|
|
@ -961,35 +971,6 @@ test('readable query fields, legacy links and browser history preserve the plan'
|
|||
expect(state(page).project.rooms).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('legacy skirting sizes migrate to one editable length and reset changed allocations', async ({
|
||||
page,
|
||||
}) => {
|
||||
const initial = defaultState();
|
||||
const legacy = {
|
||||
...initial,
|
||||
project: {
|
||||
...initial.project,
|
||||
completedBoards: ['skirting:S1'],
|
||||
skirting: { ...initial.project.skirting, stockLength: undefined, stockLengths: [3600, 2400] },
|
||||
},
|
||||
};
|
||||
await page.goto(`/#plan=${LZString.compressToEncodedURIComponent(JSON.stringify(legacy))}`);
|
||||
await expect(page.getByLabel('Skirting board length', { exact: true })).toHaveValue('3600');
|
||||
expect(state(page).project.skirting.stockLength).toBe(3600);
|
||||
expect(state(page).project.completedBoards).toEqual([]);
|
||||
const params = new URL(page.url()).searchParams;
|
||||
expect(params.get('skirting.length')).toBe('3600');
|
||||
expect(params.has('skirting.stock')).toBe(false);
|
||||
await page.getByLabel('Skirting board length', { exact: true }).fill('3000');
|
||||
await page.reload();
|
||||
await expect(page.getByLabel('Skirting board length', { exact: true })).toHaveValue('3000');
|
||||
await page.goto('/?skirting.stock=2400&skirting.stock=2400&done=skirting:S1');
|
||||
await expect(page.getByLabel('Skirting board length', { exact: true })).toHaveValue('2400');
|
||||
expect(state(page).project.completedBoards).toEqual([]);
|
||||
expect(new URL(page.url()).searchParams.get('skirting.length')).toBe('2400');
|
||||
expect(new URL(page.url()).searchParams.has('skirting.stock')).toBe(false);
|
||||
});
|
||||
|
||||
test('printing includes every room and every cut once, grouped by its source board', async ({
|
||||
page,
|
||||
}) => {
|
||||
|
|
@ -1173,6 +1154,7 @@ test('inserting and reordering walls keeps stable wall IDs and attached doors',
|
|||
if (!isMobile) {
|
||||
const source = page.locator(`.wall-row[data-wall-id="${doorWall.id}"] .wall-drag-handle`);
|
||||
const target = page.locator(`.wall-row[data-wall-id="${originalIds[0]}"]`);
|
||||
await page.locator('.wall-table').evaluate((table) => table.scrollIntoView({ block: 'start' }));
|
||||
await source.dragTo(target);
|
||||
expect(state(page).project.rooms[0].walls[0]).toEqual(doorWall);
|
||||
}
|
||||
|
|
@ -1201,9 +1183,8 @@ test('the supplied angled room keeps W12 lines and labels inside the drawing', a
|
|||
new URL('../tests/fixtures/angled-room.json', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const fixture = decodeState(`#plan=${LZString.compressToEncodedURIComponent(angledRoom)}`);
|
||||
expect(fixture.error).toBeNull();
|
||||
await page.goto(`/${encodeState(fixture.state)}`);
|
||||
const fixture = JSON.parse(angledRoom) as UrlState;
|
||||
await page.goto(`/${encodeState(fixture)}`);
|
||||
await expect(preview(page).locator('.wall-drawing')).toHaveCount(12);
|
||||
for (const width of isMobile ? [320, 390] : [1280]) {
|
||||
await page.setViewportSize({ width, height: 844 });
|
||||
|
|
@ -1317,11 +1298,10 @@ test('the supplied F8 room aligns a row seam with its fireplace corner and keeps
|
|||
new URL('../tests/fixtures/angled-room-f8.json', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
const fixture = decodeState(`#plan=${LZString.compressToEncodedURIComponent(json)}`);
|
||||
expect(fixture.error).toBeNull();
|
||||
fixture.state.view.selectedPiece = null;
|
||||
fixture.state.view.search = '';
|
||||
await page.goto(`/${encodeState(fixture.state)}`);
|
||||
const fixture = JSON.parse(json) as UrlState;
|
||||
fixture.view.selectedPiece = null;
|
||||
fixture.view.search = '';
|
||||
await page.goto(`/${encodeState(fixture)}`);
|
||||
const { floor, skirt } = plans(page);
|
||||
const corner = { x: 7607.513, y: 2160.34 };
|
||||
const atCorner = floor.pieces.filter((piece) =>
|
||||
|
|
@ -1355,7 +1335,7 @@ test('the supplied F8 room aligns a row seam with its fireplace corner and keeps
|
|||
dimensions.some(({ actual }) => Math.abs(actual * 2 - Math.round(actual * 2)) > 0.001),
|
||||
).toBe(true);
|
||||
expectHalfMillimetreCuts(dimensions);
|
||||
expect(state(page).project.rooms[0].walls).toEqual(fixture.state.project.rooms[0].walls);
|
||||
expect(state(page).project.rooms[0].walls).toEqual(fixture.project.rooms[0].walls);
|
||||
await page.keyboard.press('Escape');
|
||||
const skirtCut = skirtingCuts(skirt).find(
|
||||
(piece) =>
|
||||
|
|
@ -1399,6 +1379,7 @@ test('completed source boards and locate actions follow shared offcuts across ro
|
|||
const sourcePieces = floor.pieces.filter((piece) => piece.stockId === shared.id);
|
||||
const key = `floor:${shared.id}`;
|
||||
const group = page.locator(`.stock-cut-group[data-board-key="${key}"]`);
|
||||
await expect(group).toBeVisible();
|
||||
const groupCodes = await group
|
||||
.locator('.board-cut')
|
||||
.evaluateAll((cuts) => cuts.map((cut) => cut.getAttribute('data-cut-id')).join(' + '));
|
||||
|
|
@ -1464,6 +1445,10 @@ test('completed source boards and locate actions follow shared offcuts across ro
|
|||
.getByRole('checkbox')
|
||||
.check();
|
||||
expect(state(page).project.completedBoards).toEqual([key, skirtKey]);
|
||||
await page
|
||||
.getByRole('group', { name: 'Plan view' })
|
||||
.getByRole('button', { name: 'Skirting', exact: true })
|
||||
.click();
|
||||
expect(await preview(page).locator('.skirt-piece[data-done="true"]').count()).toBeGreaterThan(0);
|
||||
await page.getByLabel('Board width', { exact: true }).fill('201');
|
||||
expect(state(page).project.completedBoards).toEqual([]);
|
||||
|
|
@ -1508,6 +1493,7 @@ test('cuts from one board share the available width and wrap on a narrow phone',
|
|||
.filter({ has: page.locator('.board-cut:nth-child(2)') })
|
||||
.first();
|
||||
const pieces = group.locator('.board-cut');
|
||||
await expect(group).toBeVisible();
|
||||
expect(await pieces.count()).toBeGreaterThan(1);
|
||||
const first = (await pieces.nth(0).boundingBox())!;
|
||||
const second = (await pieces.nth(1).boundingBox())!;
|
||||
|
|
@ -1558,6 +1544,7 @@ for (const width of [320, 390]) {
|
|||
}) => {
|
||||
await page.setViewportSize({ width, height: 844 });
|
||||
await openWalls(page);
|
||||
await page.getByText('Advanced flooring settings', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'What is saw kerf?', exact: true }).click();
|
||||
await noHorizontalOverflow(page);
|
||||
await expect(page.getByRole('tooltip')).toBeVisible();
|
||||
|
|
|
|||
160
e2e/usability.spec.ts
Normal file
160
e2e/usability.spec.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
import { defaultState } from '../src/lib/defaults';
|
||||
import { decodeState, encodeState } from '../src/lib/url-state';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/?planner');
|
||||
await expect(page.getByRole('heading', { name: 'Room measurements', exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('new plans start random and guide custom measurements through to the cut plan', async ({
|
||||
page,
|
||||
}) => {
|
||||
await expect(page.getByRole('button', { name: 'Random', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
await expect(page.locator('#custom-shape-help')).toContainText('Click Custom');
|
||||
await page.getByRole('button', { name: 'Custom', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: 'Custom', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
await expect(page.getByLabel('Wall 1 length', { exact: true })).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Add a wall', exact: true }).click();
|
||||
await page.getByLabel('Wall 2 length', { exact: true }).fill('3000');
|
||||
await page.getByRole('button', { name: 'Add a wall', exact: true }).click();
|
||||
await page.getByLabel('Wall 3 length', { exact: true }).fill('4000');
|
||||
await page.getByRole('button', { name: 'Add closing wall', exact: true }).click();
|
||||
await expect(page.getByText('Outline closed', { exact: true })).toBeVisible();
|
||||
await expect(page.getByLabel('Wall 4 length', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Custom', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Materials', exact: true }).click();
|
||||
await expect(page.getByLabel('Board length', { exact: true })).toBeHidden();
|
||||
await page.getByRole('button', { name: 'Materials', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: 'Materials', exact: true })).toBeFocused();
|
||||
await expect(page.getByLabel('Board length', { exact: true })).toBeVisible();
|
||||
await expect(page.getByLabel('Saw kerf', { exact: true })).toBeHidden();
|
||||
await page.getByText('Advanced flooring settings', { exact: true }).click();
|
||||
await page.getByLabel('Saw kerf', { exact: true }).fill('4');
|
||||
expect(decodeState(new URL(page.url()).search).state.project.flooring.kerf).toBe(4);
|
||||
|
||||
await page.getByRole('button', { name: 'Cut plan', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Cut plan', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: 'Cut plan', exact: true })).toBeFocused();
|
||||
await expect(page.getByLabel('Find a cut or room', { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('the top switch shows one material and preserves inputs and progress across reloads', async ({
|
||||
page,
|
||||
}) => {
|
||||
const preview = page.getByRole('region', { name: 'Floor plan preview' });
|
||||
const toggle = page.getByRole('group', { name: 'Plan view', exact: true });
|
||||
await expect(toggle.getByRole('button', { name: 'Floorboards', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
expect(await preview.locator('.floor-piece').count()).toBeGreaterThan(0);
|
||||
await expect(preview.locator('.skirt-piece')).toHaveCount(0);
|
||||
await page.locator('.stock-cut-group input[type="checkbox"]').first().check();
|
||||
const project = decodeState(new URL(page.url()).search).state.project;
|
||||
await toggle.getByRole('button', { name: 'Skirting', exact: true }).click();
|
||||
await expect(preview.locator('.floor-piece')).toHaveCount(0);
|
||||
expect(await preview.locator('.skirt-piece').count()).toBeGreaterThan(0);
|
||||
await expect(preview.getByText('Flooring cuts', { exact: true })).toHaveCount(0);
|
||||
expect(decodeState(new URL(page.url()).search).state.project).toEqual(project);
|
||||
await page.reload();
|
||||
await expect(toggle.getByRole('button', { name: 'Skirting', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
await expect(preview.locator('.floor-piece')).toHaveCount(0);
|
||||
await page.goto('/?planner');
|
||||
await expect(toggle.getByRole('button', { name: 'Skirting', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
expect(decodeState(new URL(page.url()).search).state.project).toEqual(project);
|
||||
await toggle.getByRole('button', { name: 'Floorboards', exact: true }).click();
|
||||
expect(await preview.locator('.floor-piece').count()).toBeGreaterThan(0);
|
||||
await expect(preview.locator('.skirt-piece')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('saved brick plans keep their pattern and explicit links restore the selected material', async ({
|
||||
page,
|
||||
}) => {
|
||||
const state = defaultState();
|
||||
state.project.flooring.pattern = 'brick';
|
||||
state.view.material = 'skirting';
|
||||
await page.goto(`/${encodeState(state)}`);
|
||||
await expect(page.getByRole('button', { name: 'Brick', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
await expect(
|
||||
page
|
||||
.getByRole('group', { name: 'Plan view' })
|
||||
.getByRole('button', { name: 'Skirting', exact: true }),
|
||||
).toHaveAttribute('aria-pressed', 'true');
|
||||
await page.goto('/?planner');
|
||||
await expect(page.getByRole('button', { name: 'Brick', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
});
|
||||
|
||||
test('locating cuts changes material automatically and printing includes both materials', async ({
|
||||
page,
|
||||
}) => {
|
||||
const preview = page.getByRole('region', { name: 'Floor plan preview' });
|
||||
const toggle = page.getByRole('group', { name: 'Plan view', exact: true });
|
||||
await page.locator('.skirting-cuts .board-group-actions .board-locate').first().click();
|
||||
await expect(toggle.getByRole('button', { name: 'Skirting', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
expect(await preview.locator('.skirt-piece[data-highlighted="true"]').count()).toBeGreaterThan(0);
|
||||
await page.getByRole('button', { name: 'Show all pieces', exact: true }).click();
|
||||
await page.getByLabel('Find a cut or room', { exact: true }).fill('F1');
|
||||
await expect(toggle.getByRole('button', { name: 'Floorboards', exact: true })).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true',
|
||||
);
|
||||
await expect(preview.locator('.floor-piece[data-piece-id="F1"]')).toHaveAttribute(
|
||||
'data-highlighted',
|
||||
'true',
|
||||
);
|
||||
await toggle.getByRole('button', { name: 'Skirting', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: 'Show all pieces', exact: true })).toHaveCount(0);
|
||||
await preview
|
||||
.getByRole('button', { name: /^Select skirting cut / })
|
||||
.first()
|
||||
.click();
|
||||
await expect(page.getByRole('dialog')).toBeVisible();
|
||||
await page.keyboard.press('Escape');
|
||||
await page.emulateMedia({ media: 'print' });
|
||||
expect(await page.locator('.print-plan .floor-piece').count()).toBeGreaterThan(0);
|
||||
expect(await page.locator('.print-plan .skirt-piece').count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('setup and the pinned view switch fit a narrow phone', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 320, height: 844 });
|
||||
await page.getByRole('button', { name: 'Materials', exact: true }).scrollIntoViewIfNeeded();
|
||||
const heading = await page.getByRole('button', { name: 'Materials', exact: true }).boundingBox();
|
||||
const drawing = await page.getByRole('complementary', { name: 'Room drawing' }).boundingBox();
|
||||
expect(heading!.y).toBeGreaterThanOrEqual(drawing!.y + drawing!.height);
|
||||
await page
|
||||
.getByRole('group', { name: 'Plan view' })
|
||||
.getByRole('button', { name: 'Skirting', exact: true })
|
||||
.click();
|
||||
await page.getByText('Advanced flooring settings', { exact: true }).click();
|
||||
await page.getByText('Advanced skirting settings', { exact: true }).click();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320);
|
||||
const toggle = await page.getByRole('group', { name: 'Plan view' }).boundingBox();
|
||||
expect(toggle!.y).toBeGreaterThanOrEqual(0);
|
||||
expect(toggle!.y + toggle!.height).toBeLessThan(844);
|
||||
});
|
||||
Loading…
Reference in a new issue