ac29dca865
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 41s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
- Highlight tracks the last move overall (not the last word): a trailing pass/exchange now highlights nothing, so the board no longer lights up the opponent's old word after our own empty move. - Make the highlight event-driven: refreshed only on a real game event (open/refresh, opponent move, our own committed move) and dismissed the moment composing starts, so recalling a just-placed tile never re-triggers it. - Localize non-play move-history labels via new move.* catalog keys (pass/exchange/resign/timeout); the label printed the raw English action. - Clamp the zoomed board's pan at its edge (overscroll-behavior: none), removing the native rubber-band past the content. Tests: lastMoveCells unit coverage (trailing pass/exchange -> empty), i18n RU label assertions, an e2e overscroll-contract check on the zoomed viewport.
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
import { expect, test } from './fixtures';
|
|
|
|
// Item 5: zooming the board must enlarge the labels too (a magnifying-glass zoom).
|
|
// cqw is sized against the zoom-scaled board, so the font grows with the cells.
|
|
test('zoom enlarges the board labels with the board', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.getByRole('button', { name: /guest/i }).click();
|
|
await page.getByRole('button', { name: /Ann/ }).click();
|
|
|
|
const letter = page.locator('[data-cell] .letter').first();
|
|
await expect(letter).toBeVisible();
|
|
const base = await letter.evaluate((el) => parseFloat(getComputedStyle(el).fontSize));
|
|
|
|
// Double-tap an empty cell to zoom in (two synchronous clicks = a double-tap).
|
|
await page
|
|
.locator('[data-cell]:not(.filled)')
|
|
.nth(20)
|
|
.evaluate((el: HTMLElement) => {
|
|
el.click();
|
|
el.click();
|
|
});
|
|
await page.waitForTimeout(400); // let the width transition settle
|
|
|
|
const zoomed = await letter.evaluate((el) => parseFloat(getComputedStyle(el).fontSize));
|
|
expect(zoomed).toBeGreaterThan(base * 1.4);
|
|
});
|
|
|
|
// Item 4 (UX): the zoomed board clamps at its edge — no native rubber-band/overscroll past
|
|
// the content. We assert the CSS contract on the zoomed scroll container, which is
|
|
// deterministic across engines (the bounce gesture itself is a native compositor behaviour
|
|
// Playwright cannot reliably synthesize).
|
|
test('zoomed board clamps overscroll at the edge', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.getByRole('button', { name: /guest/i }).click();
|
|
await page.getByRole('button', { name: /Ann/ }).click();
|
|
|
|
// Double-tap an empty cell to zoom in.
|
|
await page
|
|
.locator('[data-cell]:not(.filled)')
|
|
.nth(20)
|
|
.evaluate((el: HTMLElement) => {
|
|
el.click();
|
|
el.click();
|
|
});
|
|
|
|
const viewport = page.locator('.viewport.zoomed');
|
|
await expect(viewport).toBeVisible();
|
|
const ob = await viewport.evaluate((el) => {
|
|
const s = getComputedStyle(el);
|
|
return { x: s.overscrollBehaviorX, y: s.overscrollBehaviorY };
|
|
});
|
|
expect(ob.x).toBe('none');
|
|
expect(ob.y).toBe('none');
|
|
});
|