From 66fc28058f2681b035f97eaf5e27cc5e5786f0e7 Mon Sep 17 00:00:00 2001 From: hided62 Date: Thu, 27 Aug 2026 05:14:55 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=EA=B2=8C=EC=9E=84=20=EC=8B=9C=EC=9E=91?= =?UTF-8?q?=20=ED=9B=84=20=EC=82=AC=EC=A0=84=20=EA=B1=B0=EB=B3=91=20?= =?UTF-8?q?=EC=88=A8=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 현재 턴 판정에 lastTurnTime을 우선 사용해 장수 삭제와 같은 가오픈 경계로 맞춘다.\n\n구형 turntime이 남은 시작 후 상태를 데스크톱과 모바일 Chromium 회귀로 고정한다. --- app/game-frontend/e2e/inGameMenus.spec.ts | 51 ++++++++++++++++++++++ app/game-frontend/src/views/MyPageView.vue | 5 ++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/app/game-frontend/e2e/inGameMenus.spec.ts b/app/game-frontend/e2e/inGameMenus.spec.ts index 817f931d..0fb1d9d7 100644 --- a/app/game-frontend/e2e/inGameMenus.spec.ts +++ b/app/game-frontend/e2e/inGameMenus.spec.ts @@ -66,6 +66,7 @@ type FixtureState = { instantRetreatAttempts?: number; instantRetreatInputs?: Array>; buildNationCandidateEnabled?: boolean; + gameStarted?: boolean; buildNationCandidateAttempts?: number; buildNationCandidateInputs?: Array>; dieOnPrestartShow?: boolean; @@ -517,6 +518,9 @@ const install = async (page: Page, state: FixtureState) => { }, meta: { turntime: '2026-01-01T00:00:00.000Z', + lastTurnTime: state.gameStarted + ? '2026-03-01T00:00:00.000Z' + : '2026-01-01T00:00:00.000Z', opentime: state.buildNationCandidateEnabled ? '2026-02-01T00:00:00.000Z' : '2025-12-01T00:00:00.000Z', @@ -2225,6 +2229,53 @@ test('사전 거병은 timeout reload 뒤 같은 ID를 재시도하고 성공 re expect(requestIds?.[2]).not.toBe(requestIds?.[1]); }); +test('게임 시작 뒤에는 구형 turntime이 남아 있어도 사전 거병을 숨긴다', async ({ page }) => { + const state: FixtureState = { + permission: 'member', + myset: 3, + buildNationCandidateEnabled: true, + gameStarted: true, + dieOnPrestartShow: false, + settingMutations: [], + accessPages: [], + }; + await install(page, state); + for (const viewport of [ + { name: 'desktop', width: 1000, height: 900 }, + { name: 'mobile', width: 390, height: 900 }, + ]) { + await page.setViewportSize(viewport); + await page.goto('my-page'); + await waitForVisualAssets(page); + + await expect(page.getByRole('button', { name: '장수 삭제' })).toHaveCount(0); + await expect(page.getByRole('button', { name: '사전 거병' })).toHaveCount(0); + const save = page.getByRole('button', { name: '설정저장' }); + await expect(save).toBeVisible(); + await save.focus(); + await expect(save).toBeFocused(); + const geometry = await save.evaluate((element) => { + const rect = element.getBoundingClientRect(); + const style = getComputedStyle(element); + return { + viewportWidth: window.innerWidth, + documentScrollWidth: document.documentElement.scrollWidth, + rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height }, + backgroundColor: style.backgroundColor, + fontSize: style.fontSize, + }; + }); + expect(geometry).toMatchObject({ + viewportWidth: viewport.width, + documentScrollWidth: viewport.width, + rect: { width: 160, height: 30 }, + backgroundColor: 'rgb(34, 85, 0)', + fontSize: '14px', + }); + await persistParityArtifact(page, `core-my-page-started-hidden-actions-${viewport.name}`, geometry); + } +}); + test('감찰부 keeps the selector interaction and shows the permission error path', async ({ page }) => { const head: FixtureState = { permission: 'head', myset: 3, settingMutations: [], accessPages: [] }; await install(page, head); diff --git a/app/game-frontend/src/views/MyPageView.vue b/app/game-frontend/src/views/MyPageView.vue index 6f8d56d4..dfd8f35f 100644 --- a/app/game-frontend/src/views/MyPageView.vue +++ b/app/game-frontend/src/views/MyPageView.vue @@ -183,7 +183,10 @@ const actionAvailability = computed(() => { const config = world.value?.config ?? {}; const constConfig = asRecord(config.const); const availableInstantAction = asRecord(constConfig.availableInstantAction ?? config.availableInstantAction); - const turnTime = meta.turntime ? new Date(String(meta.turntime)) : null; + // 엔진은 현재 턴을 lastTurnTime으로 진행하고 호환용 turntime 투영값은 유지할 수 있다. + // 장수 삭제와 같은 현재 시계를 사용해야 게임 시작 뒤 가오픈 행동이 다시 노출되지 않는다. + const rawTurnTime = meta.lastTurnTime ?? meta.turntime; + const turnTime = rawTurnTime ? new Date(String(rawTurnTime)) : null; const openTime = meta.opentime ? new Date(String(meta.opentime)) : null; const preopen = Boolean(turnTime && openTime && turnTime.getTime() <= openTime.getTime()); const npcMode = numberValue(config.npcMode ?? config.npcmode, 0);