diff --git a/app/game-frontend/e2e/tournamentBracket.spec.ts b/app/game-frontend/e2e/tournamentBracket.spec.ts index d2eff7e0..1e8b79df 100644 --- a/app/game-frontend/e2e/tournamentBracket.spec.ts +++ b/app/game-frontend/e2e/tournamentBracket.spec.ts @@ -693,6 +693,30 @@ test('tournament and betting pages expose same-row navigation tabs beside close' expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(390); }); +test('tournament and betting close only their script-opened popup window', async ({ page }, testInfo) => { + const baseURL = testInfo.project.use.baseURL; + expect(typeof baseURL).toBe('string'); + await page.goto('about:blank'); + + for (const route of ['tournament', 'betting'] as const) { + const popupPromise = page.waitForEvent('popup'); + await page.evaluate(() => window.open('about:blank', '_blank', 'noopener')); + const popup = await popupPromise; + await installFixture(popup, { tournamentStage: route === 'betting' ? 6 : 1 }); + await popup.goto(new URL(route, baseURL as string).href); + + await expect(popup.getByRole('button', { name: '창 닫기' }).first()).toBeVisible(); + expect(await popup.evaluate(() => window.opener)).toBeNull(); + + const closed = popup.waitForEvent('close'); + await popup.getByRole('button', { name: '창 닫기' }).first().click(); + await closed; + + expect(page.isClosed()).toBe(false); + expect(page.url()).toBe('about:blank'); + } +}); + test('mobile betting rankings use tabs and keep dedicated icons beside general names', async ({ page }, testInfo) => { await page.setViewportSize({ width: 390, height: 844 }); const { placedBets } = await installFixture(page, { tournamentStage: 6 }); diff --git a/app/game-frontend/src/components/tournament/TournamentPageHeader.vue b/app/game-frontend/src/components/tournament/TournamentPageHeader.vue index c7f24c70..6700d052 100644 --- a/app/game-frontend/src/components/tournament/TournamentPageHeader.vue +++ b/app/game-frontend/src/components/tournament/TournamentPageHeader.vue @@ -3,6 +3,8 @@ defineProps<{ activePage: 'tournament' | 'betting'; title: string; }>(); + +const closeWindow = (): void => window.close(); diff --git a/app/game-frontend/test/legacyButtonFamily.test.ts b/app/game-frontend/test/legacyButtonFamily.test.ts index a76f7d3e..13d49213 100644 --- a/app/game-frontend/test/legacyButtonFamily.test.ts +++ b/app/game-frontend/test/legacyButtonFamily.test.ts @@ -39,6 +39,8 @@ void describe('shared Lumen button family', () => { for (const label of ['토너먼트', '베팅장', '창 닫기']) { assert.match(tournamentHeader, new RegExp(`legacy-button[\\s\\S]{0,260}${label}`, 'u')); } + assert.match(tournamentHeader, /const closeWindow = \(\): void => window\.close\(\)/u); + assert.doesNotMatch(tournamentHeader, /custom to="\/"/u); assert.match(reservedEditor, /legacy-button legacy-button--info legacy-button--fixed-height select-command/u); assert.match(reservedEditor, /명령 선택 ▾/u); });