feat(ui): merge Exchange/Pass into one action; drop dead Tournaments tab
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 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m0s
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 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m0s
Lobby: remove the inert 🏆 Tournaments tab (it only raised a 'coming soon' toast); the lobby is back to three tabs, matching docs/FUNCTIONAL.md. Game: fold the separate 🥺 Skip (pass) tab into the 🔄 tab, now Exchange/Pass. Its dialog passes when no tile is selected (button 'Pass without exchanging') and exchanges when tiles are ('Exchange N'). The tab is no longer gated on an empty bag (pass must stay reachable in the endgame); inside the dialog tile selection is disabled while the bag is below a full rack (bagLen >= RACK_SIZE). The merge is UI-only. A pass is NOT an exchange of zero tiles: the rules allow an exchange only with a full rack left in the bag and forbid a zero-tile swap, while a pass is always legal; GCG (Poslfit) writes a pass as a bare '-' and an exchange as '-TILES'. Pass and exchange stay distinct end-to-end (wire, engine, history/GCG); the dialog dispatches the existing gateway.pass / gateway.exchange. No backend/wire/history/GCG change. Docs: docs/UI_DESIGN.md, docs/FUNCTIONAL.md (+_ru), PRERELEASE.md. Tests: ui/e2e/game.spec.ts.
This commit is contained in:
+19
-11
@@ -17,7 +17,8 @@ async function openGame(page: Page): Promise<void> {
|
||||
|
||||
test('offline shows the Connecting indicator and softly disables server actions', async ({ page }) => {
|
||||
await openGame(page);
|
||||
// The exchange/draw tab is a server action; on my turn with tiles in the bag it is live.
|
||||
// The Exchange/Pass tab is a server action; on my turn it is live (pass is always available,
|
||||
// so unlike before it no longer goes inert when the bag empties).
|
||||
const draw = page.locator('.tab').first();
|
||||
await expect(draw).toBeEnabled();
|
||||
await expect(page.getByText('Connecting…')).toHaveCount(0);
|
||||
@@ -179,25 +180,32 @@ test('history: a swipe-up close does not make a follow-up score tap jump', async
|
||||
await expect(page.locator('.boardwrap.slid')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Draw opens the exchange dialog and confirms a selection', async ({ page }) => {
|
||||
test('Exchange/Pass dialog: a selection exchanges, no selection offers a pass', async ({ page }) => {
|
||||
await openGame(page);
|
||||
await page.locator('button:has-text("🔄")').click(); // Draw tab
|
||||
await page.locator('button:has-text("🔄")').click(); // the merged Exchange/Pass tab
|
||||
await expect(page.locator('.exch')).toBeVisible();
|
||||
|
||||
// With nothing selected the confirm button offers a pass; selecting tiles flips it to an
|
||||
// exchange of that many (the two are distinct game actions behind one dialog).
|
||||
const confirm = page.locator('button.confirm');
|
||||
await expect(confirm).toHaveText('Pass without exchanging');
|
||||
await page.locator('.etile').first().click();
|
||||
await expect(page.locator('.etile.sel')).toHaveCount(1);
|
||||
await page.locator('button.confirm').click();
|
||||
await expect(confirm).toHaveText('Exchange 1');
|
||||
await confirm.click();
|
||||
await expect(page.locator('.exch')).toBeHidden();
|
||||
});
|
||||
|
||||
test('pass confirms with a tap on the fading ✅ instead of a popup', async ({ page }) => {
|
||||
test('passing goes through the Exchange/Pass dialog with no tiles selected', async ({ page }) => {
|
||||
await openGame(page);
|
||||
const pass = page.getByRole('button', { name: 'Skip' }); // the 🥺 tab (aria-label)
|
||||
await expect(pass).toBeEnabled();
|
||||
await pass.click(); // arm: 🥺 -> a fading ✅
|
||||
await pass.click(); // tap the ✅ to confirm within the window
|
||||
// The pass hands the turn over, so the control goes inert.
|
||||
await expect(pass).toBeDisabled();
|
||||
const draw = page.locator('button:has-text("🔄")'); // the merged Exchange/Pass tab
|
||||
await expect(draw).toBeEnabled();
|
||||
await draw.click();
|
||||
// No tiles selected -> the confirm button passes the turn (a pass, not an exchange of zero).
|
||||
await page.locator('button.confirm').click();
|
||||
await expect(page.locator('.exch')).toBeHidden();
|
||||
// The pass hands the turn over, so the tab goes inert.
|
||||
await expect(draw).toBeDisabled();
|
||||
});
|
||||
|
||||
test('check-word sanitises input and shows a verdict', async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user