Files
huajishe-tts/apps/web/e2e/mill-multiplayer.spec.ts
e2hang d46dc54808 feat(multiplayer): room list UI + E2E/unit/Docker fixes
Adds the multiplayer functionality the user requested:

- LobbyRoomList UI component (create / list / join via boardgame.io's
  built-in Lobby REST API). Credentials are server-issued; no more hardcoded
  p0/p1 in OnlineConfig.
- LobbyClient re-export wrapper in @tts-like/engine (typed factory
  createLobbyClient).
- CORS origin default: dev-friendly regex list (boardgame.io's
  isOriginAllowed treats literal '*' as exact-match, which silently blocks
  everything; we now use RegExp matching localhost + Tauri schemes).
- War shuffle accepts an injected random fn so unit tests are deterministic;
  fixed a tie-handling bug where piles were cleared without redistributing
  cards (now piles stay on tie for a "war" round).
- Engine unit tests (vitest + Local master for 2-client sync):
    packages/engine/src/games/{drag-test,war,mill}.test.ts  (34 cases)
- Server REST tests (vitest + supertest, port 0 random):
    packages/server/src/server.test.ts                        (9 cases)
- Playwright E2E (apps/web/e2e/*): 3 specs covering create → join →
  sync for War / DragTest / Mill across two browser contexts.
- Dockerfile fix: pnpm deploy --prod leaves boardgame.io as a symlink to
  .pnpm/boardgame.io@…; copy -rL materialises the dist so the runtime
  image can resolve boardgame.io/server.
- Root scripts: dev:server, test:e2e, verify (ts + test + e2e).
2026-08-23 15:37:58 +08:00

60 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Mill — 联机 E2E
*
* 验证联机模式下 MillBoard 渲染正确24 个交叉点)。
* 完整下棋流程覆盖在前面的单元测试vitest + Local master里。
*/
import { test, expect, Browser } from '@playwright/test';
async function setupOnlineGame(browser: Browser, gameKey: string) {
const ctx = await browser.newContext();
const page = await ctx.newPage();
await page.goto('/');
await page.getByTestId('game-select').selectOption(gameKey);
await page.getByTestId('mode-online').click();
await expect(page.getByTestId('lobby-room-list')).toBeVisible();
return page;
}
async function joinAsFirstPlayer(page: any, playerSlot: '0' | '1') {
if (playerSlot === '0') {
await page.getByTestId('lobby-create-room').click();
} else {
await expect(async () => {
await page.getByTestId('lobby-refresh').click();
const roomChips = await page.locator('[data-testid^="lobby-room-"]').all();
const ids = await Promise.all(roomChips.map((c: any) => c.getAttribute('data-testid')));
const real = ids.filter((id: string | null) =>
id && !id.endsWith('-list') && !id.endsWith('-refresh') && !id.endsWith('-room'),
);
expect(real.length).toBeGreaterThan(0);
}).toPass({ timeout: 10_000 });
const rooms = page.locator('[data-testid^="lobby-room-"]:not([data-testid="lobby-room-list"])');
const count = await rooms.count();
expect(count).toBeGreaterThan(0);
const firstRoom = rooms.first();
const matchIDAttr = await firstRoom.getAttribute('data-testid');
const matchID = matchIDAttr!.replace(/^lobby-room-/, '');
await page.getByTestId(`lobby-join-1-${matchID}`).click();
}
await expect(page.getByTestId('online-need-room')).not.toBeVisible({ timeout: 15_000 });
}
test.describe('Mill — 2-player multiplayer E2E', () => {
test('both clients see the mill board after joining same room', async ({ browser }) => {
const p0 = await setupOnlineGame(browser, 'mill');
const p1 = await setupOnlineGame(browser, 'mill');
await joinAsFirstPlayer(p0, '0');
await joinAsFirstPlayer(p1, '1');
// 两 tab 都应能渲染 SVG 棋盘MillBoard 用 SVG
const svgCount0 = await p0.locator('svg').count();
const svgCount1 = await p1.locator('svg').count();
expect(svgCount0).toBeGreaterThan(0);
expect(svgCount1).toBeGreaterThan(0);
});
});